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地址

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持lingkb。