放松的时候看看知乎,生活不解的时候问问知乎,这貌似已经成为了生活中的一种习惯,它独特翻页方式也是本人喜欢的一个原因,通过上划与下滑进行页面的翻页,不必返回再进入下一个页面,显得非常的简介并且人性化,这里就模拟知乎进行一次模拟滚动换页,希望能给想做这种效果的人一种思路,实现的大体思路就是在ScrollView中嵌套ScrollView来感应达到切换效果。利用工作之余就做了一个类似功能的小Demo,当然距离知乎还差得远,但是基本原理应该如此,如果有更好的思路,也请告知一下,在这里先初始化一个Thanks() O(∩_∩)O

如果有解析不详细的地方,也欢迎去Demo的GitHub下载一下本人的Demo:https://github.com/YRunIntoLove/YSimilarZHPullDownDemo

看一下效果图(因为图片太大,所以草草的滑动了几下,Sorry - -):

梳理层次(顺序:子视图 -> 父视图):

  • 响应滑动的ScrollView,搭载自定义的customView,能够响应滑动反馈进行上下翻页的回调,并通过Delegate告知superView进行怎么样的翻页操作,Demo中叫做SimilarZHPullDownView
  • 进行整体翻页的ScrollView,搭载SimilarZHPullDownView,通过感知上下翻页的回调,设置自身的ContentOffset更改偏移量达到翻页的效果,Demo中叫做YSimilarZHPullDownMainView

SimilarZHPullDownView

需要一个滚动视图(ScrollView)作为底层视图,响应滑动操作,加载的标签以及自定义的视图都贴到这个滚动视图上,通过代理中的相关回调获取偏移量,判断进行什么样子的滚动。
这里选择通过懒加载的方式加载这个属性,与Objective-C不太一样,正好熟悉一下Swift中的懒加载

/// 懒加载底层的滚动视图
lazy var bottomScrollView : UIScrollView  =
{//当前视图的宽度let width = self.bounds.size.width//初始化滚动视图var scrollView:UIScrollView = UIScrollView(frame:CGRectMake(0, 0, width, self.bounds.size.height))//添加自定义视图scrollView.addSubview(self.customView!)//自定义视图的的高度var height = self.customView?.bounds.size.height//头页scrollView.addSubview(self.createLable(CGRectMake(0,-1 * self.responseHeight,width,30), title: self.headerTitle))//尾页scrollView.addSubview(self.createLable(CGRectMake(0,height!,width,self.responseHeight), title:self.footerTitle))//设置ContentSize的高度,保证不小于视图的高度height = (height > self.bounds.size.height ? height : self.bounds.size.height)scrollView.contentSize = CGSize(width: self.bounds.size.width, height: height!)scrollView.delegate = self;return scrollView
}()

一个描述最小响应幅度大小的变量,上拉或者下拉达到切换状态的最小幅度,或者说上拉、下拉达到换页响应的最小幅度

/// 响应的高度,就是滑动响应的最小幅度
final let responseHeight = CGFloat(60)

一个自定义视图,将需要展示的视图赋值给这个属性即可,但是必须设置好frame

/// 中间位置的自定义视图
var customView:UIView?

其他的相关属性:

/// 代理
weak var delegate:SimilarZHPullDownViewDelegate?/// 标签上写的Title
var headerTitle = "上一篇"
var footerTitle = "下一篇"
var title = "Title"/// 类型
var type:SimilarZHPullDownType = .Default

相关的类型属性通过枚举来完成,枚举格式如下:

//滑动响应的方式
enum PullType
{case PullTypeUp   //上翻页case PullTypeDown //下翻页
}//视图的类型
enum SimilarZHPullDownType
{case Default    //默认中间页case Header     //第一页case Footer     //尾页
}

Swift是可以实现init方法的多态模式的,只不过需要用convenience 关键字来修饰一下init方法,并且这个关键词只能修饰init方法,只不过参数不一样而已,因此下面的就是便利构造方法:

//MARK: - FUNCTION
override init(frame: CGRect) {    super.init(frame: frame)
}/**
*  便利构造方法
*/
@available(iOS 8.0,*)
convenience init(frame: CGRect ,custom:UIView)
{self.init(frame:frame)self.customView = custom
}/**
*  便利构造方法
*/
@available(iOS 8.0,*)
convenience init(custom:UIView)
{self.init(frame:CGRectNull,custom:custom)
}/**
*  便利构造方法
*/
@available(iOS 8.0,*)
convenience init(custom:UIView,title:String)
{self.init(custom:custom)self.title = title
}

在加载视图的时候添加上底层的scrollView,用self.scrollView实现懒加载的实现

override func layoutSubviews(){self.addSubview(self.bottomScrollView)
}

最后这个视图的最重要的,通过底层ScrollView的代理方法,获得偏移量的垂直偏移量,通过偏移量的大小判断是否进行滚动以及滚动的方式

//MARK: -UIScrollView Delegatefunc scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {//获得偏移量let contentOffsetY = scrollView.contentOffset.y//当前响应的高度,因为下拉,所以响应距离为负数let beforeHeight = self.responseHeight * (-1)if(contentOffsetY < beforeHeight){print("我是DownView,上一页!")//表示上一页self.delegate?.similarZHPullDownView(self, pullType: .PullTypeUp)}//偏移量是视图左上角,所以垂直坐标需要-滚动视图的高度else if(contentOffsetY > (scrollView.contentSize.height - scrollView.bounds.size.height + self.responseHeight)){print("我是DownView,下一页!")//表示下一页self.delegate?.similarZHPullDownView(self, pullType: .PullTypeDown)}}

对外的传值和之前一样,用的是Delegate(委托) 传值,但是这里依旧可以使用闭包传值,以后可以尝试,定义的协议如下

protocol SimilarZHPullDownViewDelegate : class
{ /***  需要翻页进行的回调*/@available(iOS 8.0,*)func similarZHPullDownView(similarZHPullDownView : SimilarZHPullDownView , pullType:PullType)
}

这样视图就基本完成了

YSimilarZHPullDownMainView

这个视图是一个过渡作用的视图,是负责存储并且展示SimilarZHPullDownView的一个容器

定义两个对外的属性,提供默认值,第一个是第一个视图下拉出现的标签显示的String字段,第二个是最后一篇上拉出现标签显示的String字段,在外也可以通过设置来更改这两个存储属性

/***  第一篇上划以及最后一篇下滑显示的默认字样,可修改 ***/
var headerTitle:String = "已是第一篇"
var footerTitle:String = "最后一篇"

创建底层进行切换的ScrollView,这个ScrollView不能响应手动滚动,必须通过SimilarZHPullDownView的Delegate回调方法来更新contentOffset,达到切换的效果

/// 存放滚动页的主滚动页
lazy var scrollView:UIScrollView = {var scrollView:UIScrollView = UIScrollView(frame: CGRectMake(0,self.titleLabel.bounds.size.height,self.bounds.size.width,self.bounds.size.height - 50 - 64))scrollView.pagingEnabled = true//分页显示scrollView.showsVerticalScrollIndicator = false //不显示垂直滚条scrollView.scrollEnabled = false //不能滚动return scrollView
}()

SimilarZHPullDownView上面显示title属性的标签,就是Demo中显示测试I的视图

/// 显示标题的标题
lazy var titleLabel:UILabel = {var label:UILabel = UILabel(frame: CGRectMake(0,0,self.bounds.size.width,50))label.textAlignment = .Centerreturn label
}()

存储SimilarZHPullDownView对象的数据源,并在设置新值得时候进行数据的处理,是存储属性,willSet相当于Objcetive-C中的KVO,监听属性的新值

/// 存放预览视图的数组,默认为空数组,为存储属性,设置KVO
var pullViews:[SimilarZHPullDownView] = []
{willSet{self.pullViews = newValue//开始做处理self.pullViews.first?.headerTitle = self.headerTitleself.pullViews.first?.type = .Headerself.pullViews.last?.footerTitle = self.footerTitleself.pullViews.last?.type = .Footer}
}

自定义的便利构造方法,require表示继承与该类的类必须实现这个init方法,当然这里可以不用加require,只是为了了解一下这个关键字而已(调皮0.0)

//MARK: - 构造方法
override init(frame: CGRect)
{super.init(frame: frame)self.addSubview(self.titleLabel)self.addSubview(self.scrollView)
}/***  便利构造方法*/
@available(iOS 8.0,*)
required convenience init(frame: CGRect,pullViews:[SimilarZHPullDownView])
{self.init(frame:frame)self.pullViews = pullViews
}

在加载该视图的时候,设置底层滚动视图容纳域的大小以及添加数据源中所有的SimilarZHPullDownView对象

override func layoutSubviews() {//设置自身的contentSizeself.scrollView.contentSize = CGSize(width: self.bounds.size.width, height: CGFloat(self.pullViews.count) * self.bounds.size.height)self.addChildView()
}

添加所有SimilarZHPullDownView视图的方法如下

//MARK: - 功能方法/**
*  添加子视图
*/
@available(iOS 8.0,*)
func addChildView()
{//获取当前视图的高度和宽度let height = self.bounds.size.heightlet width = self.bounds.size.widthfor(var i:Int = 0 ; i < self.pullViews.count; i++){//获取存储的SimilarZHPullDownView对象let pullDownView = self.pullViews[i]pullDownView.frame = CGRectMake(0, CGFloat(i) * height, width, height - 64 - 50)//设置代理pullDownView.delegate = self//添加视图self.scrollView.addSubview(pullDownView)}self.titleLabel.text = self.pullViews.first?.title
}

实现SimilarZHPullDownView Delegate的协议方法

// MARK: - SimilarZHPullDownView Delegate
func similarZHPullDownView(similarZHPullDownView: SimilarZHPullDownView, pullType: PullType)
{//获得当前的偏移量let contentOffset = self.scrollView.contentOffset//获得索引数let index = self.pullViews.indexOf(similarZHPullDownView)var paramNumber = CGFloat(1)switch pullType{case .PullTypeUp: //上翻页guard similarZHPullDownView.type == .Header else{paramNumber = CGFloat(-1)self.pullDone(paramNumber, contentOffset: contentOffset, index: index!)break}case .PullTypeDown://下翻页guard similarZHPullDownView.type == .Footer else{paramNumber = CGFloat(1)self.pullDone(paramNumber, contentOffset: contentOffset, index: index!)break}}
}

封装之后的滚动操作

/***  滚动操作*/
@available(iOS 8.0,*)
func pullDone(paramNumber:CGFloat,var contentOffset:CGPoint,index:Int)
{contentOffset.y += (paramNumber * self.bounds.size.height)self.scrollView.setContentOffset(contentOffset, animated: true)//显示即将出现的similarZHPullDownView对象的titleself.titleLabel.text = self.pullViews[index + Int(paramNumber)].title
}

ViewController中进行加载过渡视图YSimilarZHPullDownMainView并作相关设置,Demo中的设置如下:

override func viewDidLoad() {super.viewDidLoad()self.navigationItem.title = "Yue"//创建DemoMainView对象//赋值/*** 设置表头与表位文字需要设置数据源之前 ***/demoMainView.headerTitle = "啦啦啦,我已经是第一篇了"demoMainView.footerTitle = "哈哈哈,我是最后一篇啦"demoMainView.pullViews = self.createPullDownViews()//添加视图self.view.addSubview(demoMainView)}
/***  创建测试PullDown视图*/
@available(iOS 8.0,*)
func createPullDownViews() -> [SimilarZHPullDownView]
{var views :[SimilarZHPullDownView] = []for(var i:Int = 0 ; i < 3; i++){let similarPullDownView = SimilarZHPullDownView(custom:self.createCustomView(),title:"测试\(i)")views.append(similarPullDownView)}return views
}
//随机创建UIView的对象
func createCustomView() -> UIView
{//初始化视图let view = UIImageView()//获得随机数//let count:UInt32 = arc4random_uniform(3) + UInt32(1)let count = 1//为了测试view.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height * CGFloat(count))view.image = UIImage(named: "testImage.jpg")view.contentMode = .ScaleToFillreturn view
}

这样功能基本就会像Demp中那样实现响应上下拉切换页面,理解不深,如果有错误,也请指点一下,Thanks()

iOS开发------仿知乎上下滑动切换页面相关推荐

  1. iOS开发 剖析网易新闻标签栏视图切换(addChildViewController属性介绍)

    iOS开发 剖析网易新闻标签栏视图切换(addChildViewController属性介绍) 本来仅仅是打算介绍一下addChildViewController这种方法的,正好今天朋友去换工作面试问 ...

  2. html仿今日头条下拉刷新,小程序 仿今日头条 带滑动切换的文章列表

    小程序 仿今日头条 带滑动切换的文章列表 发布时间:2018-07-19 09:41, 浏览次数:353 拿别人仿今日头条的代码做的改版, 首先感谢前辈.其次,这个代码虽然能用,但是js里还是存在一些 ...

  3. android app开发混合开发,混合开发入门 Vue结合Android/iOS开发仿京东项目App

    download:混合开发入门 Vue结合Android/iOS开发仿京东项目App 无需原生开发基础,也能完美呈现京东商城.本课程融合vue.Android.IOS等目前流行的前端和移动端技术,混合 ...

  4. Android仿抖音上下滑动切换视频

    Android仿抖音上下滑动切换视频 https://www.jianshu.com/p/af9c0e46725d  自从各大直播平台可以滑动切换直播间后,公司就出了一大波需求,还要配合各种收费,各种 ...

  5. IOS开发——仿网易新闻客户端

    IOS开发--仿网易新闻客户端 本文没有内容,传个资源 衔接地址:http://download.csdn.net/detail/u012881779/7152281 左侧导航部分: 新闻版块 订阅版 ...

  6. fragment+viewpager+tablayou实现滑动切换页面

    本文目标:实现滑动切换页面 首先,Tablayout控件就需要添加design library,在android studio中添加依赖  compile 'com.android.support:d ...

  7. 开发笔记——vue echarts图表在切换页面大小时缩成一团

    开发笔记--vue echarts图表在切换页面大小时缩成一团 添加this.$nextTick() this.$nextTick(()=>{this.getjkechertsdata(); / ...

  8. ios 顶部tab滑动实现_iOS开发之多表视图滑动切换示例(仿头条客户端)

    好长时间没为大家带来iOS开发干货的东西了,今天给大家分享一个头条新闻客户端各个类别进行切换的一个示例.在Demo中对所需的组件进行的简单封装,在封装的组件中使用的是纯代码的形式,如果想要在项目中进行 ...

  9. iOS开发——仿微信图片浏览交互的实现(向下拖拽图片退出图片浏览器)

    点击上方"iOS开发",选择"置顶公众号" 关键时刻,第一时间送达! DEMO的github地址:https://github.com/YYProgrammer ...

最新文章

  1. opencv-python实现马赛克油画漫画风格的图片
  2. InsightFace tensorRT
  3. 科大讯飞语音合成api
  4. 连接器篇(一) 低频系列
  5. 吴恩达深度学习课程deeplearning.ai课程作业:Class 1 Week 3 assignment3
  6. html双翼布局,第19题 CSS如何实现双飞翼布局?
  7. 剑指 Offer 16. 数值的整数次方
  8. ORM SQLAlchemy 简介
  9. android studio : clang++.exe: error: invalid linker name in argument '-fuse-ld=bfd
  10. python 支付宝个人账单_金融支付财务融合业务-实践分享1:订单、账单、交易流水、账套知识解构、原理解析...
  11. mysql5.0.x统计每秒增删改查替换数及系统每秒磁盘IO
  12. redhat7扩容linux,vmware中Centos 7 linux的LVM磁盘扩容
  13. mysql 自动复制_MySQL复制 自动监控脚本-阿里云开发者社区
  14. [影视源码]全民影院源码 综合影视HTML源码 无需更新搭建即可用
  15. Syong :静态代理模式
  16. matlab设计误码率,通信原理课程设计报告 数字传输系统误码率测试器的matlab实现及性能分析...
  17. Raspberry 2B Ubuntu mate 16.04 *** 完美透明代理
  18. MATLAB app designer GUI设计
  19. Python操作word插入对象
  20. [Intellij IDEA] 通过学生认证免费激活IDEA

热门文章

  1. [论文阅读] Structure-Consistent Weakly Supervised Salient Object Detection with Local Saliency Coherence
  2. python平安经_读完一本平安经是怎样的体验?
  3. docker搭建mysql主从复制
  4. JAVA 关于图片上传的代码
  5. 停车还能360全方位影像_汽车新技术:360全景环视系统技术
  6. Python对文件的创建和读写操作
  7. mysql、oracle、sqlserver各自的默认端口号
  8. iperf测试工具使用方法
  9. PC机8250芯片组串口通信以及研华PCM3614板卡串口通信程序设计
  10. 研华PCI板卡开发(5)快速入门(5)群组操作