您的位置:首页 > 移动开发

玩转【斗鱼直播APP】系列之首页布局分析实现

2016-09-18 09:01 627 查看

布局首页分析实现

完成最终效果

这个效果还是挺常见的哈



效果分析:
顶部是一个工具类,并且为了该工具类可以复用。(其他界面也用到)最好是单独封装起来



中间内容可以左右滚动,并且有分页效果。可以通过封装UIView,并且里面添加UICollectionView方式。



封装顶部的PageTitleView

封装构造函数

封装构造函数,让别人在创建对象时,就传入其实需要显示的内容
frame:创建对象时确定了frame就可以直接设置子控件的位置和尺寸
isScrollEnable:是否可以滚动。某些地方该控件是可以滚动的。
titles:显示的所有标题

// MARK:- 构造函数

init(frame: CGRect, isScrollEnable : Bool, titles : [String]) {

self.isScrollEnable = isScrollEnable

self.titles = titles

super.init(frame: frame)

}


设置UI界面

设置UI界面
添加UIScrollView,如果标题过多,则可以滚动
初始化所有的Label,用于显示标题。并且给label添加监听手势
添加顶部线和滑块的View

private lazy var scrollView : UIScrollView = {

let scrollView = UIScrollView(frame: self.bounds)

scrollView.showsHorizontalScrollIndicator = false

scrollView.scrollsToTop = false

scrollView.bounces = false

return scrollView

}()



private lazy var scrollLine : UIView = {

let scrollLine = UIView()

scrollLine.backgroundColor = kSelectTitleColor

return scrollLine

}()



private func setupUI() {

// 1.添加scrollView

addSubview(scrollView)



// 2.初始化labels

setupTitleLabels()



// 3.添加定义的线段和滑动的滑块

setupBottomlineAndScrollline()

}



private func setupTitleLabels() {



let titleY : CGFloat = 0

let titleH : CGFloat = bounds.height - kScrollLineH

let count = titles.count



for (index, title) in titles.enumerate() {

// 1.创建Label

let label = UILabel()



// 2.设置Label的属性

label.text = title

label.tag = index

label.textAlignment = .Center

label.textColor = kNormalTitleColor

label.font = UIFont.systemFontOfSize(16.0)

titleLabels.append(label)



// 3.设置label的frame

var titleW : CGFloat = 0

var titleX : CGFloat = 0

if !isScrollEnable {

titleW = bounds.width / CGFloat(count)

titleX = CGFloat(index) * titleW

} else {

let size = (title as NSString).boundingRectWithSize(CGSizeMake(CGFloat(MAXFLOAT), 0), options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName : label.font], context: nil)

titleW = size.width

if index != 0 {

titleX = CGRectGetMaxX(titleLabels[index - 1].frame) + kTitleMargin

}

}

label.frame = CGRect(x: titleX, y: titleY, width: titleW, height: titleH)



// 4.将Label添加到父控件中

scrollView.addSubview(label)



// 5.监听label的点击

label.userInteractionEnabled = true

let tapGes = UITapGestureRecognizer(target: self, action: #selector(self.titleLabelClick(_:)))

label.addGestureRecognizer(tapGes)

}

}



private func setupBottomlineAndScrollline() {

// 1.添加bottomline

let bottomline = UIView()

bottomline.frame = CGRect(x: 0, y: bounds.height - 0.5, width: bounds.width, height: 0.5)

bottomline.backgroundColor = UIColor.lightGrayColor()

addSubview(bottomline)



// 2.设置滑块的view

addSubview(scrollLine)

guard let firstLabel = titleLabels.first else { return }

let lineX = firstLabel.frame.origin.x

let lineY = bounds.height - kScrollLineH

let lineW = firstLabel.frame.width

let lineH = kScrollLineH

scrollLine.frame = CGRect(x: lineX, y: lineY, width: lineW, height: lineH)

firstLabel.textColor = kSelectTitleColor

}


封装顶部的PageCotentView

封装构造函数

封装构造函数,让别人在创建对象时,就传入其实需要显示的内容
所有用于显示在UICollectionView的Cell的所有控制器
控制器的父控制器

// MARK:- 构造函数

init(frame: CGRect, childVcs : [UIViewController], parentViewController : UIViewController) {

self.childVcs = childVcs

self.parentViewController = parentViewController



super.init(frame: frame)

}


设置UI界面内容

设置UI界面
将所有的子控制器添加到父控制器中
添加UICollectionView,用于展示内容

// MARK:- 懒加载属性

private lazy var collectionView : UICollectionView = {



// 1.创建布局

let layout = UICollectionViewFlowLayout()

layout.itemSize = self.bounds.size

layout.minimumLineSpacing = 0

layout.minimumInteritemSpacing = 0

layout.scrollDirection = .Horizontal



// 2.创建collectionView

let collectionView = UICollectionView(frame: self.bounds, collectionViewLayout: layout)

collectionView.showsHorizontalScrollIndicator = false

collectionView.pagingEnabled = true

collectionView.bounces = false

collectionView.scrollsToTop = false

collectionView.dataSource = self

collectionView.delegate = self

collectionView.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: kContentCellID)



return collectionView

}()



private func setupUI() {

// 1.添加所有的控制器

for childVc in childVcs {

parentViewController?.addChildViewController(childVc)

}



// 2.添加collectionView

addSubview(collectionView)

}


实现UICollectionView的数据源方法

在返回Cell的方法中,先将cell的contentView中的子控件都移除,防止循环引用造成问题
取出indexPath.item对应的控制器,将控制器的View添加到Cell的contentView中
// MARK:- 遵守UICollectionView的数据源

extension PageContentView : UICollectionViewDataSource {

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

return childVcs.count

}



func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

let cell = collectionView.dequeueReusableCellWithReuseIdentifier(kContentCellID, forIndexPath: indexPath)



// 移除之前的

for subview in cell.contentView.subviews {

subview.removeFromSuperview()

}



// 取出控制器

let childVc = childVcs[indexPath.item]

childVc.view.frame = cell.contentView.bounds

cell.contentView.addSubview(childVc.view)



return cell

}

}


PageTitleView点击改变PageContentView

通过代理将PageTitleView的事件传递出去
/// 定义协议

protocol PageTitleViewDelegate : class {

func pageTitleView(pageTitleView : PageTitleView, didSelectedIndex index : Int)

}



@objc private func titleLabelClick(tapGes : UITapGestureRecognizer) {

// 1.获取点击的下标志

guard let view = tapGes.view else { return }

let index = view.tag



// 2.滚到正确的位置

scrollToIndex(index)



// 3.通知代理

delegate?.pageTitleView(self, didSelectedIndex: index)

}


内部调整
// 内容滚动

private func scrollToIndex(index : Int) {

// 1.获取最新的label和之前的label

let newLabel = titleLabels[index]

let oldLabel = titleLabels[currentIndex]



// 2.设置label的颜色

newLabel.textColor = kSelectTitleColor

oldLabel.textColor = kNormalTitleColor



// 3.scrollLine滚到正确的位置

let scrollLineEndX = scrollLine.frame.width * CGFloat(index)

UIView.animateWithDuration(0.15) {

self.scrollLine.frame.origin.x = scrollLineEndX

}



// 4.记录index

currentIndex = index

}


在PageContentView中设置当前应该滚动的位置
// MARK:- 对外暴露方法

extension PageContentView {

func scrollToIndex(index : Int) {

let offset = CGPoint(x: CGFloat(index) * collectionView.bounds.width, y: 0)

collectionView.setContentOffset(offset, animated: false)

}

}


PageContentView滚动调整PageTitleView

首先在scrollViewDidScroll的代理方法中就可以监听滚动
那么滚动时,我们有哪些内容是需要传递出去呢?
1> 原来位置的Title颜色会逐渐变暗
2> 目标位置的Title颜色会逐渐变亮
3> 变化程度是和滚动的多少相关

由此得出结论:
我们一共需要获取三个值,并且将这三个值传递出去
1> 起始位置下标值
2> 目标位置下标值
3> 当前滚动的进度

图例分析(缺)
左右滑动时,下标值、进度是不同的
需要经过判断来获取,并且下标值需要防止越界问题
注意:当左滑结束时,此时再+1会出错。因此必须加上targetIndex = sourceIndex,且进度为1

实现代码
extension PageContentView : UICollectionViewDelegate {



func scrollViewWillBeginDragging(scrollView: UIScrollView) {

startOffsetX = scrollView.contentOffset.x

}



func scrollViewDidScroll(scrollView: UIScrollView) {

// 1.定义要获取的内容

var sourceIndex = 0

var targetIndex = 0

var progress : CGFloat = 0



// 2.获取进度

let offsetX = scrollView.contentOffset.x

let ratio = offsetX / scrollView.bounds.width

progress = ratio - floor(ratio)



// 3.判断滑动的方向

if offsetX > startOffsetX { // 向左滑动

sourceIndex = Int(offsetX / scrollView.bounds.width)

targetIndex = sourceIndex + 1

if targetIndex >= childVcs.count {

targetIndex = childVcs.count - 1

}



if offsetX - startOffsetX == scrollView.bounds.width {

progress = 1.0

targetIndex = sourceIndex

}



} else  { // 向右滑动

targetIndex = Int(offsetX / scrollView.bounds.width)

sourceIndex = targetIndex + 1

if sourceIndex >= childVcs.count {

sourceIndex = childVcs.count - 1

}



progress = 1 - progress

}



// 4.通知代理

delegate?.pageContentView(self, sourceIndex: sourceIndex, targetIndex: targetIndex, progress: progress)

}

}


根据滚动传入的值,调整PageTitleView
两种颜色必须使用RGB值设置(方便通过RGB实现渐变效果)
private let kNormalRGB : (CGFloat, CGFloat, CGFloat) = (85, 85, 85)

private let kSelectRGB : (CGFloat, CGFloat, CGFloat) = (255, 128, 0)

private let kDeltaRGB = (kSelectRGB.0 - kNormalRGB.0, kSelectRGB.1 - kNormalRGB.1, kSelectRGB.2 - kNormalRGB.2)



private let kNormalTitleColor = UIColor(red: 85/255.0, green: 85/255.0, blue: 85/255.0, alpha: 1.0)

private let kSelectTitleColor = UIColor(red: 255.0/255.0, green: 128/255.0, blue: 0/255.0, alpha: 1.0)


调整scrollLine&两个Label颜色渐变
// MARK:- 对外暴露方法

extension PageTitleView {

func setCurrentTitle(sourceIndex : Int, targetIndex : Int, progress : CGFloat) {

// 1.取出两个Label

let sourceLabel = titleLabels[sourceIndex]

let targetLabel = titleLabels[targetIndex]



// 2.移动scrollLine

let moveMargin = targetLabel.frame.origin.x - sourceLabel.frame.origin.x

scrollLine.frame.origin.x = sourceLabel.frame.origin.x + moveMargin * progress



// 3.颜色渐变

sourceLabel.textColor = UIColor(red: (kSelectRGB.0 - kDeltaRGB.0 * progress) / 255.0, green: (kSelectRGB.1 - kDeltaRGB.1 * progress) / 255.0, blue: (kSelectRGB.2 - kDeltaRGB.2 * progress) / 255.0, alpha: 1.0)

targetLabel.textColor = UIColor(red: (kNormalRGB.0 + kDeltaRGB.0 * progress)/255.0, green: (kNormalRGB.1 + kDeltaRGB.1 * progress)/255.0, blue: (kNormalRGB.2 + kDeltaRGB.2 * progress)/255.0, alpha: 1.0)

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: