一、创建ScrollView
swift
@objcMembers class LSLottieAnimView: UIView, UIScrollViewDelegate {
private var scrollView: UIScrollView = UIScrollView()
func addScrollView() {
scrollView.showsHorizontalScrollIndicator = false
scrollView.isPagingEnabled = true
// 自定义frame
scrollView.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
scrollView.delegate = self
// 此处创建了三个子view的宽度
scrollView.contentSize = CGSize(width: scrollView.frame.width * CGFloat(3), height: scrollView.frame.height)
let u1 = UIView()
u1.frame = scrollView.bounds
u1.backgroundColor = .black
let u2 = UIView()
u2.frame = scrollView.bounds
u2.backgroundColor = .green
let u3 = UIView()
u3.frame = scrollView.bounds
u3.backgroundColor = .yellow
scrollView.addSubview(u1)
scrollView.addSubview(u2)
scrollView.addSubview(u3)
UIApplication.shared.keyWindow?.rootViewController?.view.addSubview(scrollView)
// 添加拖拽手势识别器
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
scrollView.addGestureRecognizer(panGesture)
}
}
二、处理手势
swift
@objc func handlePan(_ gesture: UIPanGestureRecognizer) {
let scrollView = gesture.view as! UIScrollView
let translation = gesture.translation(in: scrollView)
//
let count = scrollView.subviews.count - 1 - 1;
// 禁止回弹
if (scrollView.contentOffset.x <= 0 && translation.x > 0) || (Int(scrollView.frame.width) * count <= Int(scrollView.contentOffset.x) && translation.x < 0) {
return
}
// 当setContentOffset的动画未完成时,此时isDecelerating为true
if scrollView.isDecelerating || !scrollView.isScrollEnabled {
return
}
switch gesture.state {
// case .began:
// 如果开启轮播,则停止自动轮播
// scrollView.layer.removeAllAnimations()
case .changed:
// 根据手势的偏移量进行滚动
if scrollView.contentOffset.x - translation.x <= 0 {
scrollView.contentOffset.x = 0
} else if scrollView.contentOffset.x - translation.x >= scrollView.frame.width * CGFloat(count) {
scrollView.contentOffset.x = scrollView.frame.width * CGFloat(count)
} else {
scrollView.contentOffset.x -= translation.x
}
case .ended, .cancelled, .failed:
// 获取滚动视图当前的页索引
let pageIndex = Int(scrollView.contentOffset.x / scrollView.frame.width)
// 修改后的页面索引
var willPageIndex = pageIndex
// 根据手势的速度和偏移量来判断是否切换到上一页或下一页
let velocity = gesture.velocity(in: scrollView)
if velocity.x > 0 {
// 向右滑动
} else if velocity.x < 0 {
// 向左滑动
if pageIndex < count {
willPageIndex = pageIndex + 1
}
} else {
// 根据偏移量判断是否切换到上一页或下一页
if translation.x < -scrollView.frame.width / 2 && pageIndex < count {
willPageIndex = pageIndex + 1
} else if translation.x > scrollView.frame.width / 2 && pageIndex > 0 {
willPageIndex = pageIndex - 1
}
}
// 只有修改后才需要调用setContentOffset,否则会出现scrollViewDidEndScrollingAnimation不调用的情况
if (scrollView.contentOffset.x != CGFloat(willPageIndex) * scrollView.frame.width) {
scrollView.setContentOffset(CGPoint(x: CGFloat(willPageIndex) * scrollView.frame.width, y: 0), animated: true)
scrollView.isScrollEnabled = false
}
default:
break
}
// 重置手势的偏移量
gesture.setTranslation(.zero, in: scrollView)
}
// MARK: UIScrollViewDelegate
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
scrollView.isScrollEnabled = true
}
三、注意事项
swift
1. 如果需要回弹,只需要修改如下代码即可
// 注释如下代码
if (scrollView.contentOffset.x <= 0 && translation.x > 0) || (Int(scrollView.frame.width) * count <= Int(scrollView.contentOffset.x) && translation.x < 0) {
return
}
// 修改成如下代码
case .changed:
// // 根据手势的偏移量进行滚动
// if scrollView.contentOffset.x - translation.x <= 0 {
// scrollView.contentOffset.x = 0
// } else if scrollView.contentOffset.x - translation.x >= scrollView.frame.width * CGFloat(count) {
// scrollView.contentOffset.x = scrollView.frame.width * CGFloat(count)
// } else {
scrollView.contentOffset.x -= translation.x
// }
2. 在调用setContentOffset且animated为true时,需要考虑将isScrollEnabled设置为false,等到动画完成后(scrollViewDidEndScrollingAnimation)将isScrollEnabled恢复到true,否则在动画期间仍然可以拖拽
3. 在调用setContentOffset时,如果值和之前相同,则不会触发scrollViewDidEndScrollingAnimation