zoukankan      html  css  js  c++  java
  • iOS UIScrollView 3种分页方法,间隔实现


    基础知识参考

    http://tech.glowing.com/cn/practice-in-uiscrollview/

    https://stackoverflow.com/questions/9367600/custom-uiscrollview-paging-with-scrollviewwillenddragging

    方案一 PageEnable

    http://www.cnblogs.com/JimmyBright/p/4324042.html
    http://www.jianshu.com/p/9c1be359fd1b

    关键点是:ScrollView PageEnable 翻页大小是ScrollView的bounds来的大小,在这个基础上需要一些 hacking 实现 bleeding 和 padding(即页与页之间有 padding,在当前页可以看到前后页的部分内容)

    image_1bl9u19kdktrsl05u9jf71aec9.png-101.4kB

    以下代码基于swift3,新建项目,替换ViewController内容即可运行,预览效果
    import UIKit
    
    class ViewController: UIViewController, UIScrollViewDelegate {
        
        
        private var contentScroll: UIScrollView!
        private let scrollHeight: CGFloat = 400
        private let appWidth: CGFloat = UIScreen.main.bounds.width
        private let appHeight: CGFloat = UIScreen.main.bounds.height
       
        private let padding: CGFloat = 10
        private let totalCount: Int = 9
        private var selectedIndex: Int = 0
        
        // 设置ScrollView frame 起始点和终点左边位于超出屏幕的左右两侧,
        // 从屏幕边缘到超出屏幕的终点之间的空隙,便是每两个view之间的间隔
        
        // PageEnable 按ScrollView 的frame宽度进行翻页,每次滚动frame的宽度,
        // add view 时,展示内容区域要设置在屏幕范围之内,空隙则有屏幕外的间隔形成
        
        // 既有一下关系:
        //  1. ScrollView.frame.width = 左侧间隙 + 屏幕的宽度 + 右侧间隙
        //  2. 首页的frame.origin.x 在左侧屏幕外面,是负值,保证首个元素和屏幕对齐
        //  3. 除第一个元素外,其余元素的左侧frame.origin.x 和前一个元素右侧终点x坐标重合,保证只有一个间隙,
        // 由于间隙是重合的,所以元素view宽度打满屏幕还好,如果不是打满屏幕的话点击事件处理是个麻烦事情
        
        
        override func viewDidLoad() {
            super.viewDidLoad()
            self.contentScroll = UIScrollView()
            self.contentScroll.delegate = self
            
            // 间隙的颜色
            self.contentScroll.backgroundColor = UIColor.lightGray
            self.contentScroll.isPagingEnabled = true
            
            // 计算总的view的宽度
            let totalFrameSize = (appWidth + (2*padding)) * CGFloat(totalCount)
            self.contentScroll.contentSize = CGSize( totalFrameSize, height: 0)
            
            self.contentScroll.frame = CGRect(x: -padding, y: (appHeight-scrollHeight)*0.5,  appWidth + 2*padding, height: scrollHeight)
            
            self.view.addSubview(self.contentScroll)
            
            for index in 0...(totalCount - 1) {
                let btn = UIButton()
                btn.setTitle(String(index), for: .normal)
                btn.backgroundColor = UIColor.brown
                
                // 获取scroll view 的大小
                let bounds = self.contentScroll.bounds
               
                // 设定展示内容的view的frame宽度为:屏幕宽度
                var pageFrame: CGRect = bounds
                pageFrame.size.width = pageFrame.size.width - (2*padding)
                
                // 设定展示内容的view的frame的origin位置在屏幕x坐标系的左侧起始点
                pageFrame.origin.x = (bounds.size.width*CGFloat(index)) + padding
                btn.frame = pageFrame
                
                self.contentScroll.addSubview(btn)
            }
        }
        
        func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
            
        }
        
        
    }
    

    方案二 snap

    这种方法就是在 didEndDragging 且无减速动画,或在减速动画完成时,snap 到一个整数页。核心算法是通过当前 contentOffset 计算最近的整数页及其对应的 contentOffset,通过动画 snap 到该页。这个方法实现的效果都有个通病,就是最后的 snap 会在 decelerate 结束以后才发生,总感觉很突兀。

    方案三 修改 targetContentOffset

    oc:

    http://tech.glowing.com/cn/practice-in-uiscrollview/

    - (CGPoint)nearestTargetOffsetForOffset:(CGPoint)offset
    {
        CGFloat pageSize = BUBBLE_DIAMETER + BUBBLE_PADDING;
        NSInteger page = roundf(offset.x / pageSize);
        CGFloat targetX = pageSize * page;
        return CGPointMake(targetX, offset.y);
    }
    
    - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
    {
        CGPoint targetOffset = [self nearestTargetOffsetForOffset:*targetContentOffset];
        targetContentOffset->x = targetOffset.x;
        targetContentOffset->y = targetOffset.y;
    }
    
    swift3
    public func scrollViewWillEndDragging(_ scrollView: UIScrollView ,withVelocity velocity: CGPoint, targetContentOffset: 
    UnsafeMutablePointer<CGPoint>){  
    let pageWidth = Float(appWidth + itemSpacing)
        let targetXContentOffset = Float(targetContentOffset.pointee.x)
        var newPage = Float(currentPageIndex)
          // I use this way calculate newPage:
        newPage = roundf(targetXContentOffset / pageWidth);
        
        // 以下方式在最后一页,左滑到最后一页时,左滑出现边角,然后在往右侧滑动,页面会直接跳动到倒数第二页,
        // 是由于在这里使用velocity.x判断向左右翻页并不科学
        //if velocity.x == 0 {
        //newPage = floor( (targetXContentOffset - Float(pageWidth) / 2) / Float(pageWidth))
        // + 1.0
        //} else {
        //    newPage = Float(velocity.x > 0 ? newPage + 1 : newPage - 1)
        //    if newPage < 0 {
        //        newPage = 0
        //    }
        //    if (newPage > contentWidth / pageWidth) {
        //        newPage = ceil(contentWidth / pageWidth) - 1.0
        //   }
        //}
        
        // 滑动距离太短时,没有动画效果,解决方法:
            targetContentOffset.pointee = CGPoint(x:scrollView.contentOffset.x, y: scrollView.contentOffset.y)
            
            let pageWidth = Float(appWidth + itemSpacing)
            let targetXContentOffset = Float(targetContentOffset.pointee.x)
            // let contentWidth = Float(collectionView!.contentSize.width)
        
            var newPage = Float(currentPageIndex)
            newPage = roundf(targetXContentOffset / pageWidth);
            
            let targetOffsetX = CGFloat(newPage * pageWidth)
            let newPosition = CGPoint (x: targetOffsetX, y: targetContentOffset.pointee.y)
            // 动画间隔一下避免冲突
            DispatchUtils.dispatchAfterGap {
                scrollView.setContentOffset(newPosition, animated: true)
            }
    
     }
    
    滑动距离太短时,没有动画效果
  • 相关阅读:
    redux的使用流程
    react类型检查
    将逻辑运算字符串转化为逻辑运算进行运算
    SQL 行列互换 天高地厚
    【转载】linux的IO调度算法和回写机制 天高地厚
    查询昨天的数据 天高地厚
    摘:DBA案例CPU占用100%的问题 天高地厚
    ASP.net HTTP/HTTPS自动切换 天高地厚
    网络连接和初始HTTP请求 天高地厚
    C++内存对象大会战 . 天高地厚
  • 原文地址:https://www.cnblogs.com/buoge/p/9343287.html
Copyright © 2011-2022 走看看