UIAnmator UISnap Поведение возможно с UIScrollview?

Я пытаюсь выяснить, могу ли я использовать UISnapBehavior из UIAnimator внутри UIScrollview, чтобы привязать содержимое представления прокрутки к точке. Пока мои выводы привели к тому, что это невозможно.

Чего я пытаюсь достичь

UIScrollView для "привязки" к определенной точке, когда пользователь перетаскивает представление прокрутки. Однако прокрутка должна возобновиться из защелкивающегося положения, и пользователю не нужно поднимать касание.

Apple, кажется, добивается этого с помощью редактирования фотографий в своем приложении iOS Photos. (См. Скриншот ниже)

Что я пробовал

Я попытался получить такое поведение, подключив UIPanGestureRecognizer к просмотру прокрутки и используя его скорость. Если пользователь перетаскивает к точке привязки, представление прокрутки отключит прокрутку, анимирует к точке привязки, после завершения анимации она снова включит прокрутку.

Однако это приводит к проблеме, при которой пользователь ДОЛЖЕН ПОДДЕРЖИВАТЬ подкрашивание после перетаскивания и перетаскивать представление прокрутки. Тем не менее, Apple, кажется, сделал это без необходимости тянуть вверх.

2 ответа

Решение

Я пытался имитировать приложение iOS Фото. Вот моя логика:

// CALCULATE A CONTENT OFFSET FOR SNAPPING POINT 
let snapPoint = CGPoint(x: 367, y: 0)  

// CHANGE THESE VALUES TO TEST
let minDistanceToSnap = 7.0
let minVelocityToSnap = 25.0
let minDragDistanceToReleaseSnap = 7.0
let snapDuringDecelerating = false

Этот вид прокрутки требует 3 этапа

enum SnapState {
case willSnap
case didSnap
case willRelease
}
  1. willSnap: Состояние по умолчанию. Решите, когда щелкнуть. сравнить contentOffset distance from SnapPoint with minDistanceToSnap а также scrollview velocity with minVelocityToSnap, Изменить на didSnap государство.
  2. didSnap: Вручную setContentOffset к предоставленной contextOffset(snapPoint), подсчитывать dragDistance на scrollView, Если пользователь перетаскивает больше определенного расстояния (minDragDistanceToReleaseSnap) изменить на willRelease государство.
  3. willRelease: Изменить на willSnap заявить еще раз, если distance scroll from snapPoint больше чем minDistanceToSnap,


extension ViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(scrollView: UIScrollView) {
        switch(snapState) {
            case .willSnap:
                let distanceFromSnapPoint = distance(between: scrollView.contentOffset, and: snapPoint)
                let velocity = scrollView.panGestureRecognizer.velocityInView(view)
                let velocityDistance = distance(between: velocity, and: CGPointZero)
                if distanceFromSnapPoint <= minDistanceToSnap && velocityDistance <= minVelocityToSnap && (snapDuringDecelerating || velocityDistance > 0.0) {
                    startSnapLocaion = scrollView.panGestureRecognizer.locationInView(scrollView)
                    snapState = .didSnap
                }
            case .didSnap:
                scrollView.setContentOffset(snapPoint, animated: false)
                var dragDistance = 0.0
                let location = scrollView.panGestureRecognizer.locationInView(scrollView)
                dragDistance = distance(between: location, and: startSnapLocaion)
                if dragDistance > minDragDistanceToReleaseSnap  {
                    startSnapLocaion = CGPointZero
                    snapState = .willRelease
                }
            case .willRelease:
                let distanceFromSnapPoint = distance(between: scrollView.contentOffset, and: snapPoint)
                if distanceFromSnapPoint > minDistanceToSnap {
                    snapState = .willSnap
                }
        }
    }
}

Вспомогательная функция

func distance(between point1: CGPoint, and point2: CGPoint) -> Double {
    return Double(hypotf(Float(point1.x - point2.x), Float(point1.y - point2.y)))
}

Сделал демонстрационный проект на Github: https://github.com/rishi420/SnapDrag

Примечание: проект сделан с Xcode 7.2. Возможно, вам придется немного изменить для компиляции.

Не добавляйте UIPanGestureRecognizer непосредственно в UIScrollView. Скорее добавьте его в представление контейнера, затем в селекторе установите UIScrollView contentOffset вручную.

Отключите взаимодействие с самим UIScrollView или используйте делегат, чтобы предотвратить взаимодействие непосредственно с представлением прокрутки.

Другие вопросы по тегам