我是靠谱客的博主 紧张咖啡,最近开发中收集的这篇文章主要介绍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实现新闻首页导航的全部内容,希望文章能够帮你解决swift4.2实现新闻首页导航所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(92)

评论列表共有 0 条评论

立即
投稿
返回
顶部