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

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值