[iOS]swift开发实现滚动切换Tab导航

2023-12-22 14:37:03 浏览数 (2)

iOS开发中经常会遇到上方有一条Tab切换导航栏,点击Tab可以切换下方的页面显示。当Tab栏内元素显示超出一屏时就需要滚动显示,用户点击靠近边缘的item时就需要把屏幕外的元素滚动到屏幕内,以供用户选择,如果不滚动,那么用户就认为他点击的可能就是最后一个item,影响用户体验。具体实现就是scrollView。

大致效果如下:

先看下整个类的代码实现:

代码语言:swift复制
import UIKit

protocol TabScrollViewDelegate : NSObjectProtocol {

    func didSelectOneTab(tabTitle:String,tabIndex:Int)

}

class TabScrollView: UIView {

/** 当前选中的Tab*/

    var selectTab:String=""

    /** 未选中状态下的标题颜色*/

    var normalTitleColor : UIColor = .white

    /** 选中状态下的标题颜色*/

    var selectedTitleColor : UIColor = .white

    /** 未选中状态下的标题字号*/

    var normalTitleFontSize : CGFloat  =13.0

    /** 选中状态下的标题字号*/

    var selectedTitleFontSize : CGFloat = 18.0

    /** 每个item之间的间距  */

    var itemSpacing :CGFloat = 16.0

    /** 选中Tab时候是否添加一个指示条*/

    var isShowBottomIndicatorLine : Bool = true

    /** 选中Tab时候添加指示条的颜色*/

    var indicatorLineColor : UIColor = .white

    /** 选中Tab时候添加指示条宽度,最多跟标题齐宽*/

    var indicatorWidth : CGFloat = 16.0

    /** 选中Tab时候添加指示条高度*/

    var indicatorHeight : CGFloat = 2.0

    /** 用户点击回传信息*/

    weak var delegate:TabScrollViewDelegate?

    var tabTitles: [String]?{

        didSet{

            guard let tabTitles = tabTitleselse{return}

            addTabItems(titleArray: tabTitles)

        }

    }

    private lazy var menuScrollView:UIScrollView = {

        let scrollView =UIScrollView(frame: .zero)

        scrollView.showsVerticalScrollIndicator = false;

        scrollView.showsHorizontalScrollIndicator = false;
        scrollView.alwaysBounceHorizontal=true;
       return scrollView

    }()

    override init(frame: CGRect) {

        super.init(frame: frame)

        backgroundColor = .white

        addChildViews()

    }

    //MARK: - 添加子视图

    private func addChildViews(){

        addSubview(menuScrollView)

        menuScrollView.frame = bounds

    }

    private func addTabItems(titleArray:[String]){

        let stackView =UIStackView()

        stackView.axis= .horizontal

        stackView.alignment= .center

        stackView.spacing=itemSpacing

        menuScrollView.addSubview(stackView)

        stackView.translatesAutoresizingMaskIntoConstraints = false

        stackView.heightAnchor.constraint(equalToConstant: menuScrollView.frame.height).isActive = true

        stackView.leftAnchor.constraint(equalTo: menuScrollView.leftAnchor).isActive = true



        for(index,menuItem)intitleArray.enumerated() {

            let itemStackView =UIStackView()

            itemStackView.axis= .vertical;

            itemStackView.alignment= .center;

            itemStackView.distribution= .equalSpacing;

            itemStackView.spacing=2.0;

            itemStackView.tag= index  100;

            let tapGesture =UITapGestureRecognizer(target:self,action:#selector(clickToSelectOneModule(tapGesture:)))

            itemStackView.addGestureRecognizer(tapGesture);

            stackView.addArrangedSubview(itemStackView);



            let menuBtn =UIButton();

            menuBtn.setTitle(menuItem,for: .normal);

            if(menuItem ==selectTab)

            {

                menuBtn.titleLabel?.font= .boldSystemFont(ofSize:selectedTitleFontSize);

                menuBtn.setTitleColor(normalTitleColor,for: .selected);

            }

            else

            {

                menuBtn.titleLabel?.font= .systemFont(ofSize:normalTitleFontSize);

                menuBtn.contentEdgeInsets = UIEdgeInsets(top: (frame.size.height - normalTitleFontSize) / 2.0, left: 0, bottom: (frame.size.height - normalTitleFontSize) / 2.0, right: 0);

                menuBtn.setTitleColor(normalTitleColor,for: .normal);

            }





            menuBtn.setContentHuggingPriority(UILayoutPriority(98), for: .horizontal);

            itemStackView.addArrangedSubview(menuBtn);

            menuBtn.isUserInteractionEnabled=false;

            //            看是否选中

            if(menuItem ==selectTab)

            {

                if(isShowBottomIndicatorLine)

                {

                    let lineView =UIView();

                    lineView.backgroundColor=indicatorLineColor;

                    itemStackView.addArrangedSubview(lineView);

                    lineView.translatesAutoresizingMaskIntoConstraints = false;

                    lineView.widthAnchor.constraint(equalToConstant: indicatorWidth).isActive = true;

                    lineView.heightAnchor.constraint(equalToConstant: indicatorHeight).isActive = true;

                    lineView.setContentHuggingPriority(UILayoutPriority(99),for: .horizontal);

                    lineView.centerXAnchor.constraint(equalTo: itemStackView.centerXAnchor).isActive=true;

                }



            }

        }

        stackView.layoutIfNeeded();

        menuScrollView.contentSize = CGSize(width: stackView.frame.width, height: menuScrollView.frame.height);

    }

    //MARK: 选中某个模块

    @objc private func clickToSelectOneModule(tapGesture:UITapGestureRecognizer)

    {

        guard let gestureView = tapGesture.view,letactiveModules =tabTitleselse{return}

        let moduleIndex = gestureView.tag-100;

        let activeModule = activeModules[moduleIndex];

        selectTab= activeModule;

        var scrollVisibleIndex =0;

        var needScroll =true;

        //

        if let previousView = gestureView.superview?.viewWithTag(moduleIndex -1 100)

        {

            if let nextView = gestureView.superview?.viewWithTag(moduleIndex  1 100)

            {

                //如果当前点击的模块前后模块都完整展示,那么就不需要做偏移

                if(previousView.frame.origin.x>=menuScrollView.contentOffset.x&& (menuScrollView.contentOffset.x>= (nextView.frame.origin.x - (menuScrollView.frame.width- nextView.frame.width))))

                {

                    needScroll =false;

                }

                else

                {

                    if(gestureView.frame.origin.x>=menuScrollView.contentOffset.x menuScrollView.frame.width*0.5)

                    {

                        scrollVisibleIndex = nextView.tag;

                    }

                    else

                    {

                        scrollVisibleIndex = previousView.tag;

                    }

                }

            }

            else

            {

                //点击最后一个

                scrollVisibleIndex = gestureView.tag;

            }

        }

        else

        {

            //那就是点的第一个

            scrollVisibleIndex = gestureView.tag;

        }



        while menuScrollView.subviews.count>0{

            menuScrollView.subviews.last?.removeFromSuperview();

        }

        addTabItems(titleArray: activeModules);

        if let boxStackView =menuScrollView.subviews.last,letvisibleItem = boxStackView.viewWithTag(scrollVisibleIndex),needScroll

        {

            menuScrollView.scrollRectToVisible(visibleItem.frame,animated:true);

        }

        print("选中模块=(activeModule)");

        delegate?.didSelectOneTab(tabTitle: activeModule,tabIndex: moduleIndex);

    }

    requiredinit?(coder:NSCoder) {

        fatalError("init(coder:) has not been implemented")

    }

}

使用方法:

代码语言:swift复制
let tabScrollView = TabScrollView(frame: CGRectMake(16.0, 200.0, UIScreen.main.bounds.width - 32.0, 28.0))
        tabScrollView.backgroundColor = .orange
        view.addSubview(tabScrollView)
        tabScrollView.selectTab = "推荐"
        tabScrollView.tabTitles = ["推荐","大厂动态","时政新闻","民生","军事","财经","历史故事","经济胡侃","今日说法"]
        tabScrollView.delegate = self

然后实现代理方法,用户选中某个Tab的信息就会在代理方法中回传,可以依据回传的信息做出相应的UI调整。

代码语言:swift复制
func didSelectOneTab(tabTitle: String, tabIndex: Int) {
        print("选中 (tabTitle),index = (tabIndex)")
    }

需要说明的是,里边的一些属性设置想要生效的话必须在对tabTitles赋值前就自定义好。希望这些拙劣的代码能帮助你。

0 人点赞