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
}
willSnap:
Состояние по умолчанию. Решите, когда щелкнуть. сравнитьcontentOffset distance from SnapPoint with minDistanceToSnap
а такжеscrollview velocity with minVelocityToSnap
, Изменить наdidSnap
государство.didSnap:
ВручнуюsetContentOffset
к предоставленнойcontextOffset(snapPoint)
, подсчитыватьdragDistance
наscrollView
, Если пользователь перетаскивает больше определенного расстояния (minDragDistanceToReleaseSnap
) изменить наwillRelease
государство.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 или используйте делегат, чтобы предотвратить взаимодействие непосредственно с представлением прокрутки.