对于仿照新闻首页的页面,已经有比较好用的OC版本,现在我们来写一个swift版本的。
设备:xcode 10 2 语言:swift 4 2
效果图:我们
对于仿照新闻首页的页面,已经有比较好用的OC版本,现在我们来写一个swift版本的。
设备:xcode 10.2 语言:swift 4.2
效果图:
我们先创建一个多控制器的导航栏,直接上代码:
// // JHSBarItemView.swift // ScrollBarController // // Created by yaojinhai on 2019/4/15. // Copyright © 2019年 yaojinhai. All rights reserved. // import UIKit enum BarItemBorderType { case defualt case barItem case maskView case customItem } protocol JHSBarItemViewDelegate: NSObjectProtocol { func selectedIndexItem(view: JHSBarItemView,index: Int) -> Void } class JHSBarItemView: UIView { var minMargin: CGFloat = BarConfig.minMargin; weak var delegate: JHSBarItemViewDelegate? var lineBarView: UIView! var barType = BarItemBorderType.defualt { didSet{ configBarType(); removeBarItem(idx: selectedIndex); } } var selectedIndex = 0; var titles: [String]!{ didSet{ caculateItemSize(); } } private var titlesView: UICollectionView! private var cachesSize = [String:CGSize](); override init(frame: CGRect) { super.init(frame: frame); createContentView(); } convenience init(frame: CGRect,titles: [String]) { self.init(frame: frame); self.titles = titles; createContentView(); caculateItemSize(); } func progressWidth() -> Void { } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension JHSBarItemView: UICollectionViewDataSource,UICollectionViewDelegateFlowLayout { private func caculateItemSize() -> Void { guard let itemTitles = titles ,itemTitles.count > 0 else { return; } var maxWidth: CGFloat = 0; for item in itemTitles { let size = item.textSize(size: CGSize(width: width, height: height), font: BarConfig.normalFont); cachesSize[item] = CGSize(width: size.width, height: height); maxWidth += size.width; } let gap = (width - maxWidth) / CGFloat(itemTitles.count + 1); minMargin = max(gap, BarConfig.minMargin); titlesView.reloadData(); removeBarItem(idx: selectedIndex); } private func createContentView() -> Void { if titlesView != nil { return; } let layout = UICollectionViewFlowLayout(); layout.minimumLineSpacing = 0; layout.minimumInteritemSpacing = 0; layout.scrollDirection = .horizontal; titlesView = FMBaseCollectionView(frame: .init(x: 0, y: 0, width: width, height: height), collectionViewLayout: layout); addSubview(titlesView); titlesView.register(BarItemViewCell.self, forCellWithReuseIdentifier: "title"); titlesView.delegate = self; titlesView.dataSource = self; let lineView = createView(rect: .init(x: 0, y: height - 1, width: width, height: 1)); lineView.backgroundColor = rgbColor(rgb: 234); } // MARK: - collection view delegate and dataSource func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return titles?.count ?? 0; } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return UIEdgeInsets(top: 0, left: minMargin, bottom: 0, right: minMargin); } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { if titles == nil { return CGSize.zero; } let item = titles[indexPath.row]; if let size = cachesSize[item] { return CGSize(width: size.width + minMargin, height: height); } let size = titles[indexPath.row].textSize(size: CGSize.init(width: width, height: height), font: BarConfig.normalFont); let newSize = CGSize(width: size.width + minMargin, height: height); cachesSize[item] = size; return newSize; } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "title", for: indexPath) as! BarItemViewCell; cell.titleLabel.text = titles[indexPath.row]; cell.titleLabel.isHighlighted = selectedIndex == indexPath.row; return cell; } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { didSelected(idx: indexPath.row); delegate?.selectedIndexItem(view: self, index: indexPath.row); } func didSelected(idx: Int) -> Void { let indexPath = IndexPath(item: idx, section: 0); titlesView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true); let cell = titlesView.cellForItem(at: indexPath) as? BarItemViewCell; cell?.titleLabel.isHighlighted = true; cell?.RunAnimation(); removeBarItem(idx: indexPath.row); if selectedIndex != indexPath.row { let preCell = titlesView.cellForItem(at: .init(row: selectedIndex, section: 0)) as? BarItemViewCell; preCell?.titleLabel.isHighlighted = false; preCell?.RunAnimation(); selectedIndex = indexPath.row; } } } extension JHSBarItemView { private func removeBarItem(idx: Int) { if barType == .barItem { let size = getMaxWidthAt(index: idx); lineBarView.frame = .init(x: size.width, y: height - 2, width: size.height, height: 2); }else if barType == .maskView { let size = getMaxWidthAt(index: idx); lineBarView.frame = .init(x: size.width - minMargin/2, y: 0, width: size.height + minMargin, height: height); } } func getMaxWidthAt(index: Int) -> CGSize { if titles == nil || titles.count == 0 { return CGSize.zero; } var maxWidth: CGFloat = minMargin; var sizeWidth: CGFloat = cachesSize[titles[0]]!.width; if index > 0 { for item in 1...index { let title = titles[item]; let size = cachesSize[title]!; maxWidth += size.width + minMargin; sizeWidth = size.width; } } return CGSize(width: maxWidth + minMargin/2, height: sizeWidth); } private func configBarType() -> Void { if barType == .barItem { if lineBarView == nil { lineBarView = createView(rect: .init(x: 0, y: height - 2, width: 30, height: 2)); lineBarView.backgroundColor = UIColor.red; lineBarView.layer.cornerRadius = 2; lineBarView.layer.masksToBounds = true; } titlesView.addSubview(lineBarView); }else if barType == .maskView { if lineBarView == nil { lineBarView = createView(rect: .init(x: 0, y: 0, width: 30, height: height)); lineBarView.backgroundColor = UIColor.green.withAlphaComponent(0.2); lineBarView.isUserInteractionEnabled = false; } titlesView.addSubview(lineBarView); }else{ titlesView?.removeFromSuperview(); } } } class BarItemViewCell: UICollectionViewCell { var titleLabel: UILabel! override init(frame: CGRect) { super.init(frame: frame); titleLabel = createLabel(rect: bounds, text: ""); titleLabel.textAlignment = .center; titleLabel.textColor = BarConfig.normalColor; titleLabel.highlightedTextColor = BarConfig.hlightedColor; titleLabel.font = BarConfig.normalFont; } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func RunAnimation(animation: Bool = true) -> Void { self.titleLabel.font = self.titleLabel.isHighlighted ? BarConfig.hlightedFont : BarConfig.normalFont; } }
这个封装了导航栏的操作,并且实现了自动刷新功能。我们也可以自己扩展实现。
我们来定义一个控制器:
// // JHSBarController.swift // ScrollBarController // // Created by yaojinhai on 2019/4/15. // Copyright © 2019年 yaojinhai. All rights reserved. // import UIKit protocol JHSBarControllerDelegate: NSObjectProtocol { func barControllerAt(controller: JHSBarController, index: Int) -> UIViewController; func barControllerForTitle(controller: JHSBarController,index: Int) -> String? func numberOfController(controller: JHSBarController) -> Int } class JHSBarController: JHSBaseViewController { private var topBarView: JHSBarItemView! private var cachesController = [Int:UIViewController](); private var cachesTitles = [Int:String](); private var countOfContrller = 0; weak var delegate: JHSBarControllerDelegate? var currentViewController: UIViewController{ return getControllerAt(idx: selectedIndex); } var selectedIndex: Int { get { let offSet = contentScrollView.contentOffset; let index = Int((offSet.x + 10)/width()); let idx = max(0, min(index, countOfContrller - 1)); return idx; } set{ let idx = max(0, min(newValue, countOfContrller - 1)); toScrollAtIndex(atIndx: idx); } } override func viewDidLoad() { super.viewDidLoad() topBarView = JHSBarItemView(frame: .init(x: 0, y: 64, width: width(), height: 40), titles: ["音乐","视频","旅游","新闻"]); topBarView.delegate = self; topBarView.barType = .maskView; addView(tempView: topBarView); configContentView(); } func reloadData() -> Void { cachesController.removeAll(); cachesTitles.removeAll(); countOfContrller = delegate?.numberOfController(controller: self) ?? 0; setBarTitles(); contentScrollView.setContentOffset(.init(x: selectedIndex.cgFloat * width(), y:0), animated: false); contentScrollView.contentSize = .init(width: countOfContrller.cgFloat * width(), height: 0); addSubController(); } private func setBarTitles() -> Void { var titles = [String](); for idx in 0..<countOfContrller { let tempTitme = delegate?.barControllerForTitle(controller: self, index: idx); cachesTitles[idx] = tempTitme ?? ""; titles.append(tempTitme ?? ""); } topBarView.titles = titles; } func isInScreen(rect: CGRect) -> Bool { let offset = contentScrollView.contentOffset; let bounds = contentScrollView.convert(.init(x: offset.x, y: offset.y, width: width(), height: contentScrollView.height), to: self.view); return bounds.intersects(rect); } private func toScrollAtIndex(atIndx: Int) -> Void { let isRight = atIndx < selectedIndex; let currentCtr = currentViewController; let currentRect = currentViewController.view.frame; currentCtr.view.frame.origin.x = atIndx.cgFloat * width(); contentScrollView.contentOffset = .init(x: atIndx.cgFloat * width(), y: 0); let atCtroller = getControllerAt(idx: atIndx); let orginRect = atCtroller.view.frame; atCtroller.view.frame.origin.x += isRight ? -width() : width(); contentScrollView.bringSubviewToFront(currentCtr.view); UIView.animate(withDuration: 0.3, animations: { currentCtr.view.frame.origin.x += isRight ? self.width() : -self.width(); atCtroller.view.frame = orginRect; }) { (finshed) in currentCtr.view.frame = currentRect; } } private func addSubController() -> Void { let start = max(selectedIndex - 1, 0); let end = max(min(selectedIndex + 1, countOfContrller - 1), 0); for idx in start...end { _ = getControllerAt(idx: idx); } } private func getControllerAt(idx: Int) -> UIViewController { var controller = cachesController[idx]; if controller == nil { controller = delegate?.barControllerAt(controller: self, index: idx); cachesController[idx] = controller; } let rect = CGRect(x: idx.cgFloat * width(), y: 0, width: width(), height: contentScrollView.height); controller?.view.frame = rect; if controller!.view.superview == nil { contentScrollView.addSubview(controller!.view); } if controller!.parent == nil { addChild(controller!); } if let scrollView = controller?.view as? UIScrollView { scrollView.contentOffset.y = 0; } if let subViews = controller?.view.subviews { for item in subViews { guard let scroll = item as? UIScrollView else{ continue; } scroll.contentOffset.y = 0; } } return controller!; } func configContentView() -> Void { setContentScrollView(rect: CGRect.init(x: 0, y: topBarView.maxY, width: width(), height: height() - topBarView.height)); contentScrollView.delegate = self; contentScrollView.isPagingEnabled = true; } func scrollViewDidScroll(_ scrollView: UIScrollView) { addSubController(); } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { for item in children { if !isInScreen(rect: item.view.frame) { item.removeFromParent(); item.view.removeFromSuperview(); } } addSubController(); topBarView.didSelected(idx: selectedIndex); } func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { scrollViewDidEndDecelerating(scrollView); } func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { if !decelerate { scrollViewDidEndDecelerating(scrollView); } } } extension JHSBarController: JHSBarItemViewDelegate{ func selectedIndexItem(view: JHSBarItemView, index: Int) { selectedIndex = index; } }
我们只要继承这个就可以了:下面我们看使用方法:
// // MainViewController.swift // ScrollBarController // // Created by yaojinhai on 2019/4/15. // Copyright © 2019年 yaojinhai. All rights reserved. // import UIKit class MainViewController: JHSBarController { override func viewDidLoad() { super.viewDidLoad() delegate = self; reloadData(); } } extension JHSBarController: JHSBarControllerDelegate { func barControllerAt(controller: JHSBarController, index: Int) -> UIViewController { let ctrl = DetialViewController() ctrl.index = index; return ctrl; } func barControllerForTitle(controller: JHSBarController, index: Int) -> String? { return "第\(index)个页面"; } func numberOfController(controller: JHSBarController) -> Int { return 5; } }
最后付上demo地址
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持好代码网。