Потяните вверх и опустите UIView с изменением положения x

редактировать

Это то, что я хочу визуализировать (игнорируйте уродливую красную линию, это просто указывает на движение UIView):

Я хочу иметь UIView, который инициализируется в середине экрана. После этого я хочу подтолкнуть его вверх, и гравитация потянет его вниз, пока он не исчезнет с экрана. Мой старый вопрос работает с UIPushBehaviour, UIDynamicBehaviour и UIGravityBehaviour (см. Ниже). Мэтт отметил, что UIPushBehaviour, возможно, не правильный выбор, так как он не очень хорошо работает на всех экранах, доступных в iOS.

Я могу сделать это с помощью функции UIView.animate, но она действительно статична и не выглядит естественной. С UIPushBehaviour, UIDynamicBehaviour и UIGravityBehaviour это выглядит действительно хорошо, но величина UIPushBehaviour не может быть рассчитана для каждого размера экрана, чтобы дать одинаковую конечную точку положения x и y UIView.

Вопрос

Как я могу инициализировать UIView в середине экрана, "поднять" этот UIView (с некоторым изменением положения x) и позволить гравитации (или что-то еще) потянуть его вниз, пока он не исчезнет с экрана? Важно, чтобы изменение положения x и y было одинаковым для каждого размера экрана.

Ниже мой старый вопрос

у меня есть UIPushBehaviour с instantaneous как режим, в котором я нажимаю некоторые UIViewвокруг. Чем больше размер экрана, тем меньше он толкает.
у меня тоже есть UIDynamicItemBehavior с resistance установлен в 1Я думаю, что это одна из основных причин, по которой он отличается в зависимости от размера экрана (поправьте меня, если я ошибаюсь).

Я хочу функцию, которая будет толкать UIView до одной и той же конечной точки с одинаковой скоростью, продолжительностью и конечной точкой независимо от размера экрана.

Я попытался сделать относительную величину без удачи:

Для iPhone 5S, скажем, magnitude из 0.5 коснется UIView от середины к вершине. Я хотел рассчитать величину на всех устройствах, как это:

let y = 0.5 / 520 // 5S screen height
magnitude = self.view.frame.height * y

Для iPhone 8 у него совсем другой выход и он не работает. Читая документы, я думал, что пойму это. Я думал, что 1 величина представляет 100 пикселей, но это явно не тот случай.
Есть ли способ, которым я могу вычислить величину, например, чтобы переместить UIView с середины на право?

Я сделал проект здесь. Есть черный UIView на iPhone 5 это все дошло до краев, но не на iPhone 8.

1 ответ

Решение

Решение

Вам нужно масштабировать величину толчка относительно размера экрана, чтобы ваш обзор всегда заканчивался в одном и том же месте. Для этого нужно настроить UIPushBehavior"s pushDirection вектор работает довольно хорошо. В этом случае я установил направление нажатия пропорционально границам вида и уменьшил его на постоянный коэффициент.

let push = UIPushBehavior(items: [pushView], mode: .instantaneous)
let pushFactor: CGFloat = 0.01
push.pushDirection = CGVector(dx: -view.bounds.width * pushFactor, dy: -view.bounds.height * pushFactor)
animator.addBehavior(push)

Возможно, вам придется настроить некоторые константы, чтобы получить именно ту анимацию, которую вы хотите. Константы, которые вы можете настроить:

  • Сила тяжести (в настоящее время 0,3)
  • Фактор толчка (в настоящее время 0,01)

В зависимости от ваших потребностей, вам может понадобиться масштабировать величину гравитации, пропорциональную размеру экрана.

Примечание. Эти константы необходимо будет изменять в зависимости от размера анимированного представления, поскольку UIKit Dynamics рассматривает размер представления как его массу. Если ваш вид должен иметь динамический размер, вам нужно масштабировать ваши константы в соответствии с размером анимированного представления.

Изменить в отношении комментариев на оригинальный вопрос:

  • Представления разных размеров. Как я уже упоминал в моей заметке выше, вам необходимо применить дополнительный фактор для учета "массы" представлений. Что-то вроде view.frame.height * view.frame.width * someConstant должно хорошо работать.

  • Размер экрана iPad: в настоящее время pushFactor применяется как к dx а также dy компоненты вектора. Поскольку у iPad разные пропорции, вам нужно разделить их на две константы, может быть xPushFactor а также yPushFactor, что может объяснить различия в соотношении сторон.

Примеры

iPhone 8

GIF решения на экране iPhone 8

iPhone SE

GIF решения на размер iPhone SE

Полный исходный код детской площадки

Скопируйте и вставьте этот код в игровую площадку Swift, чтобы увидеть его в действии. Я включил размеры различных экранов iPhone, поэтому просто раскомментируйте размер, который вы хотите легко протестировать на разных устройствах. Большая часть интересного / релевантного кода находится в viewDidAppear,

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    let pushView = UIView()
    var animator: UIDynamicAnimator!

    override func viewDidLoad() {
        super.viewDidLoad()

        view.frame = CGRect(x: 0, y: 0, width: 568, height: 320) // iPhone SE
//        view.frame = CGRect(x: 0, y: 0, width: 667, height: 375) // iPhone 8
//        view.frame = CGRect(x: 0, y: 0, width: 736, height: 414) // iPhone 8+
//        view.frame = CGRect(x: 0, y: 0, width: 812, height: 375) // iPhone X

        view.backgroundColor = .white
        let pushViewSize = CGSize(width: 200, height: 150)
        pushView.frame = CGRect(x: view.bounds.midX - pushViewSize.width / 2, y: view.bounds.midY - pushViewSize.height / 2, width: pushViewSize.width, height: pushViewSize.height)
        pushView.backgroundColor = .red
        view.addSubview(pushView)

        animator = UIDynamicAnimator(referenceView: self.view)
        let dynamic = UIDynamicItemBehavior()
        dynamic.resistance = 1
        animator.addBehavior(dynamic)

    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        let gravity = UIGravityBehavior(items: [pushView])
        gravity.magnitude = 0.3
        animator.addBehavior(gravity)

        let push = UIPushBehavior(items: [pushView], mode: .instantaneous)
        let pushFactor: CGFloat = 0.01
        push.pushDirection = CGVector(dx: -view.bounds.width * pushFactor, dy: -view.bounds.height * pushFactor)
        animator.addBehavior(push)

    }

}

let vc = ViewController()
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = vc.view
Другие вопросы по тегам