为UITableView创建粘性标头
- *本教程使用Swift 2.3
我是Rep的产品和工程负责人,Rep是一个有影响力的市场,品牌和有影响力的人可以在市场营销活动中进行协作。 我决定分享如何在应用程序中构建此功能。 (对于这里的任何产品猎人,请随时追捕我们:)!)
一段时间以来,我一直在寻找一种简单的解决方案,并且严格避免出现可疑的骇客程序。 在使用XCode几个小时之后,我想出了一个很棒的方法来解决这个问题。 我将为文章提要构建UI。 外观如下:
现在,进入一些代码。 让我们添加标题,背景图像视图,文章图标和背景Alpha层。 我以编程方式使用自动布局对所有视图进行布局,但是可以根据需要随意创建视图。
我们首先需要添加所需的初始化程序和属性:
类CategoryHeaderView:UIView {
var imageView:UIImageView!
var colorView:UIView!
var bgColor = UIColor(红色:235/255,绿色:96/255,蓝色:91/255,alpha:1)
var titleLabel = UILabel()
var articleIcon:UIImageView!
init(frame:CGRect,title:String){
self.titleLabel.text = title.uppercaseString
super.init(frame:框架)
}
需要初始化吗?(编码器aDecoder:NSCoder){
fatalError(“ init(coder :)尚未实现”)
}
}
接下来,使用autolayout以编程方式对所有视图进行布局,并在init()末尾调用此setUpView()函数。 我们开始从后到前布置视图。 顺序为imageView,colorView,titleLabel,articleIcon。
func setUpView(){
self.backgroundColor = UIColor.whiteColor()
imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(imageView)
colorView = UIView()
colorView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(colorView)
让约束:[NSLayoutConstraint] = [
imageView.topAnchor.constraintEqualToAnchor(self.topAnchor),
imageView.leadingAnchor.constraintEqualToAnchor(self.leadingAnchor),
imageView.trailingAnchor.constraintEqualToAnchor(self.trailingAnchor),
imageView.bottomAnchor.constraintEqualToAnchor(self.bottomAnchor),
colorView.topAnchor.constraintEqualToAnchor(self.topAnchor),
colorView.leadingAnchor.constraintEqualToAnchor(self.leadingAnchor),
colorView.trailingAnchor.constraintEqualToAnchor(self.trailingAnchor),
colorView.bottomAnchor.constraintEqualToAnchor(self.bottomAnchor)
]
NSLayoutConstraint.activateConstraints(约束)
imageView.image = UIImage(名称:“ testBackground”)
imageView.contentMode = .ScaleAspectFill
colorView.backgroundColor = bgColor
colorView.alpha = 0.6
titleLabel.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(titleLabel)
让titlesConstraints:[NSLayoutConstraint] = [
titleLabel.centerXAnchor.constraintEqualToAnchor(self.centerXAnchor),
titleLabel.topAnchor.constraintEqualToAnchor(self.topAnchor,常数:28),
]
NSLayoutConstraint.activateConstraints(titlesConstraints)
titleLabel.font = UIFont.systemFontOfSize(15)
titleLabel.textAlignment = .Center
articleIcon = UIImageView()
articleIcon.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(articleIcon)
让imageConstraints:[NSLayoutConstraint] = [
articleIcon.centerXAnchor.constraintEqualToAnchor(self.centerXAnchor),
articleIcon.centerYAnchor.constraintEqualToAnchor(self.centerYAnchor,常数:6),
articleIcon.widthAnchor.constraintEqualToConstant(40),
articleIcon.heightAnchor.constraintEqualToConstant(40)
]
NSLayoutConstraint.activateConstraints(imageConstraints)
articleIcon.image = UIImage(名称:“文章”)
}
以编程方式对任何视图使用自动布局时,通常的模式是首先初始化视图,将其translatesAutoresizingMaskIntoConstraints属性设置为false,将其添加到视图,设置约束,激活约束,然后设置其所有属性,字体,文本,图像等等
我们首先将imageView固定到视图的顶部,顶部,底部和底部边缘。 然后,将colorView添加到图像视图的顶部,并将其alpha设置为0.6。 然后,我们添加titleLabel和文章Icon并激活所有约束。 将图像视图内容模式设置为scaleAspectFill很重要,稍后您将看到原因。
不要忘记在我们的init()函数中添加此行,否则视图将无法构建:
init(frame:CGRect,title:String){
self.titleLabel.text = title.uppercaseString
super.init(frame:框架)
setUpView()
}
好的,这就是我们的自定义视图。 现在,设置我们的视图控制器。 跳转到ViewController.swift文件,并添加一个tableview属性和一个自定义标头属性。
类ViewController:UIViewController {
var tableView:UITableView!
var headerView:CustomHeaderView!
var headerHeightConstraint:NSLayoutConstraint!
覆盖func viewDidLoad(){
super.viewDidLoad()
}
}
我添加了headerHeightConstraint属性,因为我们将在滚动时更改表格视图的高度,并需要保留对其的引用。
接下来,使用autolayout设置自定义标头。
func setUpHeader(){
headerView = CustomHeaderView(框架:CGRectZero,标题:“文章”)
headerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(headerView)
headerHeightConstraint = headerView.heightAnchor.constraintEqualToConstant(150)
headerHeightConstraint.active = true
让约束:[NSLayoutConstraint] = [
headerView.topAnchor.constraintEqualToAnchor(view.topAnchor),
headerView.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor),
headerView.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor)
]
NSLayoutConstraint.activateConstraints(约束)
}
再次,我们遵循程序化自动布局约定。 我们将标头固定在视图控制器的顶部,前端和后端,并给我们的height约束属性提供一个恒定值150。
让我们布置tableView:
func setUpTableView(){
tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
让约束:[NSLayoutConstraint] = [
tableView.topAnchor.constraintEqualToAnchor(headerView.bottomAnchor),
tableView.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor),
tableView.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor),
tableView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor)
]
NSLayoutConstraint.activateConstraints(约束)
tableView.registerClass(UITableViewCell.self,forCellReuseIdentifier:“ cell”)
tableView.dataSource =自我
}
让我们在viewDidLoad()中调用这两个函数
覆盖func viewDidLoad(){
super.viewDidLoad()
view.backgroundColor = UIColor.whiteColor()
setUpHeader()
setUpTableView()
}
目前,由于我们尚未设置表格视图数据源,因此会收到错误消息。 让我们在类的末尾添加扩展名以摆脱这些错误。 您可以复制粘贴整个部分,因为它并不是本教程的真正部分,而只是基本设置。
扩展ViewController:UITableViewDataSource {
func numberOfSectionsInTableView(tableView:UITableView)-> Int {
返回1
}
func tableView(tableView:UITableView,numberOfRowsInSection部分:Int)-> Int {
返回20
}
func tableView(tableView:UITableView,cellForRowAtIndexPath indexPath:NSIndexPath)-> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(“ cell”,forIndexPath:indexPath)
cell.textLabel?.text =“商品\(indexPath.row)”
返回单元
}
}
好的,现在就构建并运行该应用程序,它应该是这样的:
现在很好了,标题视图和表视图均已设置并且可以正常工作,以了解本教程的业务逻辑。
由于表视图本质上是滚动视图,因此它可以符合滚动视图委托方法。 让我们在setUpTableView()方法的末尾添加此行
tableView.delegate =自我
这将产生一个错误,非常需要添加UITableViewDelegate扩展以及UIScrollViewDelegate扩展。 复制并粘贴到课程末尾
扩展ViewController:UIScrollViewDelegate {
func scrollViewDidScroll(scrollView:UIScrollView){
打印(scrollView.contentOffset.y)
}
func scrollViewDidEndDragging(scrollView:UIScrollView,willDecelerate减速:Bool){
}
func scrollViewDidEndDecelerating(scrollView:UIScrollView){
}
}
扩展ViewController:UITableViewDelegate {
}
我们实际上不需要UITableViewDelegate,我们只是添加了它来消除错误。 所有的魔术都发生在UIScrollViewDelegate中
每当滚动表视图时,都会调用scrollViewDidScroll方法。 这是操纵标头视图高度的好地方。 如果scrollView.contentOffset.y 0,则表示我们正在向上推。
如果我们向下拉,则要拉伸标题视图。 这真的很简单。 让我们将以下代码行添加到scrollViewDidScroll
func scrollViewDidScroll(scrollView:UIScrollView){
如果scrollView.contentOffset.y <0 {
self.headerHeightConstraint.constant + = abs(scrollView.contentOffset.y)
}
}
我们将偏移量添加到headerViewConstraint的高度。 让我们尝试拉伸视图。
如您所见,当我们拉动时,标题会伸展。 我们还看到图像与标题视图的框架成比例。 这是因为我们将内容模式设置为宽高比填充,并且免费提供了不错的行为!
哦哦! 当我们放开时,我们会看到标题视图停留在那里。 如果我们继续将其拉低,则其延伸范围更大。
放开时,我们希望标题视图恢复到其原始高度。 委托方法scrollViewDidEndDecelerating和scrollViewDidEndDragging是将视图重新动画化的理想位置,但是首先让我们创建animateHeader方法。
在setUpTableView方法下面添加此方法 在ViewController类中:
func animateHeader(){
self.headerHeightConstraint.constant = 150UIView.animateWithDuration(0.4,延迟:0.0,usingSpringWithDamping:0.7,initialSpringVelocity:0.5,选项:.CurveEaseInOut,动画:{
self.view.layoutIfNeeded()
},完成:无)
}
我们将高度约束设置回我们的原始高度150,并通过0.4秒的阻尼使视图动画化。
在scrollViewDidEndDecelerating和scrollViewDidEndDragging中都调用animateHeader()
func scrollViewDidEndDragging(scrollView:UIScrollView,willDecelerate减速:Bool){
如果self.headerHeightConstraint.constant> 150 {
animateHeader()
}
}
func scrollViewDidEndDecelerating(scrollView:UIScrollView){
如果self.headerHeightConstraint.constant> 150 {
animateHeader()
}
}
现在,如果您构建并运行,则在向下拉动表格视图后,标题将弹回其位置!
如果您想要可折叠的标题视图,我们需要做更多的数学运算。 标准导航控制器高度为64点。 让我们添加到scrollViewDidScroll方法中,以使标题在向上滚动时可折叠 。
func scrollViewDidScroll(scrollView:UIScrollView){
如果scrollView.contentOffset.y <0 {
self.headerHeightConstraint.constant + = abs(scrollView.contentOffset.y)
}否则,如果scrollView.contentOffset.y> 0 && self.headerHeightConstraint.constant> = 65 {
self.headerHeightConstraint.constant-= scrollView.contentOffset.y / 100
如果self.headerHeightConstraint.constant <65 {
self.headerHeightConstraint.constant = 65
}
}
}
因为我们不希望标题视图上移太快,所以我们将其除以100(如果需要,可以使用此值进行播放)。 最后检查确保如果我们向上滚动太快,标题视图将保持在64点(尽管我将其设置为65)。
生成并运行。 那真是太酷了,但是文章图标阻碍了标题的发展。 让我们添加一些alpha方法来操纵颜色视图和文章图标视图的alpha,以便在滚动时背景不再是半透明的,并且图标也消失了。
将这些方法添加到CustomHeaderView()类:
func decrementColorAlpha(offset:CGFloat){
如果self.colorView.alpha <= 1 {
让alphaOffset =(offset / 500)/ 85
self.colorView.alpha + = alphaOffset
}
}
func decrementArticleAlpha(offset:CGFloat){
如果self.articleIcon.alpha> = 0 {
让alphaOffset = max((offset-65)/85.0,0)
self.articleIcon.alpha = alphaOffset
}
}
func增量ColorAlpha(偏移量:CGFloat){
如果self.colorView.alpha> = 0.6 {
让alphaOffset =(offset / 200)/ 85
self.colorView.alpha-= alphaOffset
}
}
func增量ArticleAlpha(偏移量:CGFloat){
如果self.articleIcon.alpha <= 1 {
让alphaOffset = max((offset-65)/ 85,0)
self.articleIcon.alpha = alphaOffset
}
}
这些方法只是处理一些数学问题。 让我们在scrollViewDidScroll中调用它们。
func scrollViewDidScroll(scrollView:UIScrollView){
如果scrollView.contentOffset.y <0 {
self.headerHeightConstraint.constant + = abs(scrollView.contentOffset.y)
headerView.incrementColorAlpha(self.headerHeightConstraint.constant)
headerView.incrementArticleAlpha(self.headerHeightConstraint.constant)
}否则,如果scrollView.contentOffset.y> 0 && self.headerHeightConstraint.constant> = 65 {
self.headerHeightConstraint.constant-= scrollView.contentOffset.y / 100
headerView.decrementColorAlpha(scrollView.contentOffset.y)
headerView.decrementArticleAlpha(self.headerHeightConstraint.constant)
如果self.headerHeightConstraint.constant <65 {
self.headerHeightConstraint.constant = 65
}
}
}
func scrollViewDidEndDragging(scrollView:UIScrollView,willDecelerate减速:Bool){
如果self.headerHeightConstraint.constant> 150 {
animateHeader()
}
}
func scrollViewDidEndDecelerating(scrollView:UIScrollView){
如果self.headerHeightConstraint.constant> 150 {
animateHeader()
}
}
而已! 您现在有了一个带有可折叠动画的漂亮的可伸缩页眉!
您可以在我的github jjjeeerrr111上找到该项目
jjjeeerrr111 / StickyHeader
StickyHeader –带有UITableView的Stickey标头 github.com
**使用的文章图标由Shane Herzog创建
在Twitter或Instagram上关注我
推特:@JeremySharvit
Instagram的:@JeremySh