这个菜单选择的代码 点击滚动 滑动滚动
//
// HWPageMenuView.swift
// swift项目
//
// Created by mac on 2019/10/29.
// Copyright © 2019 mac. All rights reserved.
//
import UIKit
public enum HWMenuAligment:Int {
case Left = 0
case Center
}
open class HWPageMenuView: UIView {
open var customHeight:CGFloat = 44.0
open var items:Array = []
open var normalTextColor = UIColor.gray{
didSet{
self.buttons.forEach { (btn) in
btn.setTitleColor(self.normalTextColor, for: .normal)
}
}
}
// open var
open var selectedTextColor = UIColor.black {
didSet{
self.buttons.forEach { (btn) in
btn.setTitleColor(self.selectedTextColor, for: .selected)
btn.setTitleColor(self.selectedTextColor, for: .highlighted)
}
}
}
open var normalTextFont = UIFont.systemFont(ofSize: 14) {
didSet{
self.buttons.forEach { (btn) in
if btn != self.selectedBtn {
btn.titleLabel?.font = self.normalTextFont
}
}
}
}
open var selectedTextFont = UIFont.systemFont(ofSize: 18){
didSet{
self.selectedBtn.titleLabel?.font = self.normalTextFont
}
}
open var borderColor = UIColor.blue {
didSet{
self.selectLayer.backgroundColor=borderColor.cgColor
}
}
open var borderSize = CGSize(width: 30, height: 2) {
didSet{
self.selectLayer.frame = CGRect(origin: self.selectLayer.frame.origin, size:borderSize )
}
}
open var itemMargin:CGFloat = 5
open var aligment:HWMenuAligment = .Center //只有当总宽度小于容器宽度才有效
//private
//如果需要selectedIndex值。请调用SYContainerViewController的selectedIndex属性
private var selectedIndex = 0
private var scrollView:UIScrollView = {
var scroll = UIScrollView()
scroll.scrollsToTop=false
scroll.bounces=false
scroll.showsVerticalScrollIndicator=false
scroll.showsHorizontalScrollIndicator=false
scroll.contentInset=UIEdgeInsets.zero
scroll.backgroundColor=UIColor.clear
return scroll
}()
private var selectLayer:CALayer = {
var layer = CALayer ()
layer.backgroundColor = UIColor.blue.cgColor
layer.frame = CGRect(x: 0, y: 0, width: 30, height: 2)
return layer
}()
private var buttonFrames:Array=[]
private var selectedBtn:UIButton!
private var buttons:Array = []
var block:((_ selectedIndex:Int) -> Void)!
convenience init(block:@escaping ((_ selectedIndex:Int) -> Void)) {
self.init()
self.addSubview(self.scrollView)
self.scrollView.layer.addSublayer(self.selectLayer)
self.selectLayer.backgroundColor=self.borderColor.cgColor
self.block=block
}
func setItems(_ items:Array) {
self.items = items
self.buttonFrames=[]//清空
self.buttons.forEach { (btn) in
btn.removeFromSuperview()
}
self.buttons=[]//清空
self.items.forEach { (title) in
let btn = UIButton()
btn.setTitle(title, for: .normal)
btn.addTarget(self, action: #selector(self.btnClick(sender:)), for: .touchUpInside)
btn.setTitleColor(self.normalTextColor, for: .normal)
btn.setTitleColor(self.selectedTextColor, for: .selected)
btn.setTitleColor(self.selectedTextColor, for: .highlighted)
btn.titleLabel?.font = self.normalTextFont
btn.titleLabel?.textAlignment = .center
btn.tag=self.buttons.count//刚好一一对应
self.scrollView.addSubview(btn)
self.buttons.append(btn)
let key = title as NSString
let frame = key.boundingRect(with: CGSize(width: 0, height: 30), options: [.usesLineFragmentOrigin,.usesFontLeading], attributes: [NSAttributedString.Key.font:self.selectedTextFont], context: nil)
self.buttonFrames.append(frame.size.width)
// frame = key.boundingRect(with: CGSize(width: 0, height: 30), options: [NSStringDrawingOptions.usesLineFragmentOrigin,NSStringDrawingOptions.usesFontLeading], attributes: [NSAttributedString.Key.font : self.sel]?, context: <#T##NSStringDrawingContext?#>)
}
//设置默认选项
self.selectedBtn=self.buttons.first
self.selectedBtn.isSelected = true
self.selectedBtn.titleLabel?.font = self.selectedTextFont
//重新布局
self.setNeedsLayout()
}
func setSelectedItemIndex(_ index:Int) {
if selectedIndex == index {
return
}
self.selectedIndex=index
self.selectedBtn.isSelected = false
HWAnimation.scaleAnimation(layer: self.selectedBtn.layer, from: 1.1, to: 1.0, duration: 0.35, remove: false, repeats: 1)
self.selectedBtn.titleLabel?.font = self.normalTextFont
self.selectedBtn = self.buttons[index]
self.selectedBtn.isSelected = true
self.selectedBtn.titleLabel?.font = self.selectedTextFont
HWAnimation.scaleAnimation(layer: self.selectedBtn.layer, from: 0.9, to: 1.0, duration: 0.35, remove: false, repeats: 1)
self.updateOffset(btn: self.selectedBtn)
}
@objc func btnClick(sender:UIButton) {
if self.selectedBtn == sender {
return
}
self.setSelectedItemIndex(sender.tag)
block(sender.tag)
}
//设置选中的button居中
func updateOffset(btn:UIButton) {
let offsetX:CGFloat = btn.center.x+self.frame.origin.x - self.center.x;
var point:CGPoint
if (offsetX < 0){
point = CGPoint(x: 0, y: 0)
}else if (offsetX > (scrollView.contentSize.width - self.bounds.size.width)){
point = CGPoint(x:scrollView.contentSize.width - self.bounds.size.width , y: 0)
}else{
point = CGPoint(x: offsetX, y: 0)
}
UIView.animate(withDuration: 0.4) {
self.scrollView.contentOffset=point
}
self.selectLayer.position = CGPoint(x: self.selectedBtn.center.x, y: self.frame.size.height-self.selectLayer.frame.size.height-1)
}
open override func layoutSubviews() {
super.layoutSubviews()
self.scrollView.frame=self.bounds
let sum = self.buttonFrames.reduce(0) { $0 + $1 }
if self.aligment == .Left || sum>self.frame.size.width {
var x:CGFloat = itemMargin , height = self.frame.size.height
for (index,item) in self.buttons.enumerated() {
item.frame=CGRect(x: x, y: 0, width: self.buttonFrames[index]+itemMargin, height: height)
x=item.frame.maxX
}
self.scrollView.contentSize=CGSize(width: x, height: height)
}else{
var x:CGFloat = 0 , height = self.frame.size.height , width = self.frame.size.width/CGFloat(self.buttonFrames.count)
for (_,item) in self.buttons.enumerated() {
item.frame=CGRect(x: x, y: 0, width: width, height: height)
x=item.frame.maxX
}
self.scrollView.contentSize=self.frame.size
}
self.selectLayer.position = CGPoint(x: self.selectedBtn.center.x, y: self.frame.size.height-self.selectLayer.frame.size.height-1)
}
}
这个是 添加控制器的代码
//
// HWPageViewController.swift
// swift项目
//
// Created by mac on 2019/10/29.
// Copyright © 2019 mac. All rights reserved.
//
import UIKit
let HWCellId = "HWCellId"
class HWPageViewController: UIViewController,UICollectionViewDataSource,UICollectionViewDelegate {
private var collectionView:UICollectionView!
private lazy var flowLayout=UICollectionViewFlowLayout()
open var viewControllers:Array=[]
open var selectedIndex = 0
open var vcSelectedIndex: Int? {
didSet {
self.setSelectedIndex(index: self.vcSelectedIndex!)
}
}
open var currentController:UIViewController{
get{
return self.viewControllers[self.selectedIndex]
}
}
//可能会被自定义
// open lazy var menuView = HWPageMenuView {
// [unowned self]
// (index) in
// self.willSelectControllerAtIndex(index: index)
// self.didSelectControllerAtIndex(index: index)
//
// }
open lazy var menuView = HWPageMenuView.init { (index) in
self.willSelectControllerAtIndex(index: index)
self.didSelectControllerAtIndex(index: index)
}
// MARK: 生命周期方法
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.currentController.beginAppearanceTransition(true, animated: animated)
}
open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.currentController.endAppearanceTransition()
self.updateCollectViewSelection()
}
open override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.currentController.beginAppearanceTransition(false, animated: animated)
}
open override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.currentController.endAppearanceTransition()
}
open override func viewDidLoad() {
super.viewDidLoad()
if self.responds(to: #selector(setter: edgesForExtendedLayout)) {
self.edgesForExtendedLayout = []
}
self.extendedLayoutIncludesOpaqueBars = false
self.setupUI()
}
open override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let size = self.view.frame.size
self.menuView.frame=CGRect(x: 0, y: 0, width: size.width, height: self.menuView.customHeight)
self.flowLayout.itemSize = CGSize(width: size.width, height: size.height-self.menuView.frame.maxY)
self.collectionView.frame = CGRect(x: 0, y: self.menuView.frame.maxY, width: size.width, height: size.height-self.menuView.frame.maxY)
}
open override var shouldAutomaticallyForwardAppearanceMethods: Bool{
return false
}
// MARK: custom func
private func setupUI() {
self.view.addSubview(self.menuView)
self.flowLayout.scrollDirection = .horizontal
self.flowLayout.minimumLineSpacing = 0
self.flowLayout.minimumInteritemSpacing = 0
self.collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: self.flowLayout)
self.view.addSubview(self.collectionView)
self.collectionView.isPagingEnabled=true
if #available(iOS 10.0, *) {
self.collectionView.isPrefetchingEnabled = true
}
self.collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: HWCellId)
self.collectionView.delegate=self
self.collectionView.dataSource=self
self.collectionView.showsHorizontalScrollIndicator = false
self.collectionView.showsVerticalScrollIndicator = false
self.collectionView.scrollsToTop = false
self.collectionView.backgroundColor=UIColor.white
}
open func setupVCsDataSources(viewControllers:Array) {
if viewControllers.count == 0 {
assert(true, "viewControllers不能为空")
return
}
self.viewControllers = viewControllers
self.viewControllers.forEach { (vc) in
self.addChild(vc)
}
let items = self.viewControllers.map { (vc:UIViewController) -> String in
return vc.title ?? ""
}
self.menuView.setItems(items)
self.menuView.setSelectedItemIndex(0)
self.reLoadDataSources()
}
open func reLoadDataSources() {
assert(self.viewControllers.count != 0, "缺少子视图,请调用 setupVCsDataSources(:)函数进行初始化")
self.collectionView.reloadData()
}
//设置当前选中项
open func setSelectedIndex(index:Int) {
if self.viewControllers.count>index {
selectedIndex = index
self.menuView.setSelectedItemIndex(index)
self.updateCollectViewSelection()
}
}
open func willSelectControllerAtIndex(index:Int) {
self.selectedIndex = index
self.updateCollectViewSelection()
}
open func didSelectControllerAtIndex(index:Int) {
}
func updateCollectViewSelection() {
let size = self.view.bounds.size
let offsetX = size.width * CGFloat(selectedIndex)
self.collectionView.contentOffset = CGPoint(x: offsetX, y: 0)
}
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.viewControllers.count
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HWCellId, for: indexPath)
cell.contentView.subviews.forEach { (view) in
view.removeFromSuperview()
}
let subView = self.viewControllers[indexPath.row].view!
subView.frame=cell.bounds
cell.contentView.addSubview(subView)
return cell
}
public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let vc = self.viewControllers[indexPath.row]
vc.beginAppearanceTransition(true, animated: true)
vc.endAppearanceTransition()
}
public func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let vc = self.viewControllers[indexPath.row]
vc.beginAppearanceTransition(false, animated: true)
vc.endAppearanceTransition()
}
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let size = self.view.bounds.size
let index = Int(scrollView.contentOffset.x / size.width)
selectedIndex = index
self.menuView.setSelectedItemIndex(index)
self.didSelectControllerAtIndex(index: index)
}
}
外界使用时 只需要继承 HWPageViewController 并实现 self.setupVCsDataSources(viewControllers: arr)这个方法 前提将添加的控制器 添加到 arr数组里面,并暴露了 多个属性可供设置比如 下划线的颜色 高度,宽度,选择文字的颜色 字体大小,默认选中哪个控制器 等。水平有限,不足之处还望指正。