swift4.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地址
以上是 swift4.2实现新闻首页导航 的全部内容, 来源链接: utcz.com/z/311838.html