@@ -14,10 +14,12 @@ import UIKit
1414/// - normal: 默认
1515/// - gradient: 渐变颜色
1616/// - transfrom: 放大
17+ /// - cover: 遮罩
1718public enum LCSlideMenuTitleStyle {
1819 case normal
1920 case gradient
2021 case transfrom
22+ case cover
2123}
2224
2325/// 选择菜单指示器风格
@@ -54,6 +56,12 @@ open class LCSlideMenu: UIView {
5456 /// Item的间距
5557 private var itemMargin : CGFloat = 15.0
5658
59+ /// 遮罩高度
60+ private var coverHeight : CGFloat = 25.0
61+
62+ /// 遮罩内部间距
63+ private var coverInsetMargin : CGFloat = 10.0
64+
5765 /// 选中的索引
5866 fileprivate var itemSelectedIndex : Int = 0
5967
@@ -66,6 +74,14 @@ open class LCSlideMenu: UIView {
6674 /// 伸缩动画的偏移量
6775 fileprivate let indicatorAnimatePadding : CGFloat = 8.0
6876
77+ // MARK: - 懒加载视图
78+
79+ /// 指示器视图
80+ public lazy var indicatorView : UIView = UIView ( )
81+
82+ /// 内容视图
83+ fileprivate lazy var mainScrollView : UIScrollView = UIScrollView ( )
84+
6985 /// 菜单栏
7086 fileprivate lazy var tabScrollView : UIScrollView = {
7187 let tabScrollView = UIScrollView ( frame: self . bounds)
@@ -75,16 +91,10 @@ open class LCSlideMenu: UIView {
7591 return tabScrollView
7692 } ( )
7793
78- /// 内容视图
79- fileprivate lazy var mainScrollView : UIScrollView = UIScrollView ( )
80-
81- /// 指示器视图
82- public lazy var indicatorView : UIView = UIView ( )
83-
8494 /// 底部长线
8595 private lazy var scrollLine : UIView = { [ unowned self] in
8696 let scrollLine = UIView ( )
87- let scrollLineH : CGFloat = 0.5 // 底部长线的高度
97+ let scrollLineH : CGFloat = 0.5
8898 scrollLine. backgroundColor = UIColor . lightGray
8999 scrollLine. frame = CGRect ( x: 0 , y: self . bounds. height - scrollLineH, width: self . bounds. width, height: scrollLineH)
90100 return scrollLine
@@ -96,6 +106,13 @@ open class LCSlideMenu: UIView {
96106 blurView. frame = self . bounds
97107 return blurView
98108 } ( )
109+
110+ /// 遮罩视图
111+ private lazy var coverView : UIView = {
112+ let coverView = UIView ( )
113+ coverView. backgroundColor = coverColor
114+ return coverView
115+ } ( )
99116
100117 /// 标题字体
101118 public var itemFont : UIFont = UIFont . systemFont ( ofSize: 13 ) {
@@ -104,6 +121,33 @@ open class LCSlideMenu: UIView {
104121 }
105122 }
106123
124+ /// 是否显示指示器视图
125+ public var isShowIndicatorView : Bool = false {
126+ didSet{ // 监听数值isShowIndicatorView的改变
127+ if !isShowIndicatorView {
128+ indicatorView. isHidden = true
129+ }
130+ configIndicatorView ( )
131+ }
132+ }
133+
134+ /// 是否需要遮罩
135+ public var isNeedMask : Bool = false {
136+ didSet { // 监听数值isNeedMask的改变
137+ if !isNeedMask {
138+ coverView. isHidden = true
139+ }
140+ configCoverView ( )
141+ }
142+ }
143+
144+ /// 遮罩颜色
145+ public var coverColor : UIColor = UIColor ( white: 0.4 , alpha: 0.5 ) {
146+ didSet {
147+
148+ }
149+ }
150+
107151 /// 选中颜色
108152 public var selectedColor : UIColor = . red {
109153 didSet {
@@ -118,14 +162,14 @@ open class LCSlideMenu: UIView {
118162 }
119163 }
120164
121- /// 下标距离底部距离
165+ /// 指示器距离底部距离
122166 public var bottomPadding : CGFloat = 2.0 {
123167 didSet {
124168
125169 }
126170 }
127171
128- /// 下标高度
172+ /// 指示器高度
129173 public var indicatorHeight : CGFloat = 2.0 {
130174 didSet{
131175
@@ -151,7 +195,7 @@ open class LCSlideMenu: UIView {
151195 addSubview ( scrollLine)
152196
153197 configTabScrollView ( )
154- configIndicatorView ( )
198+ // configIndicatorView()
155199 }
156200
157201
@@ -213,19 +257,23 @@ open class LCSlideMenu: UIView {
213257 // 遍历titles
214258 for (index, title) in titles. enumerated ( ) {
215259
216- // 创建Label, 并设置其相关属性
260+ // 1. 创建Label, 并设置其相关属性
217261 let label = UILabel ( )
218262 label. text = title
219263 label. font = itemFont
264+ label. isUserInteractionEnabled = true
265+ label. textAlignment = . center
266+
220267 // 如果标签索引为:选中索引, 设置Label颜色为: 选中颜色, 否则为: 未选中颜色
221268 label. textColor = index == itemSelectedIndex ? selectedColor : unSelectedColor
222- label. isUserInteractionEnabled = true
223269
224270 // 计算title长度
225271 // 根据文字来计算宽度
226272 let size = ( title as NSString ) . size ( withAttributes: [ NSAttributedStringKey . font : itemFont] )
227-
228- label. frame = CGRect ( x: originX, y: 0 , width: size. width, height: self . bounds. height)
273+
274+ // 2.计算位置
275+ label. frame = CGRect ( x: originX, y: 0 , width: size. width + itemMargin, height: self . bounds. height)
276+
229277 // 添加tap手势
230278 let tap = UITapGestureRecognizer ( target: self , action: #selector( labelClicked ( _: ) ) )
231279 label. addGestureRecognizer ( tap)
@@ -235,12 +283,11 @@ open class LCSlideMenu: UIView {
235283
236284 originX = label. frame. maxX + itemMargin * 2
237285 }
238- // 设置scrollView的滚动范围
286+ // 3. 设置scrollView的滚动范围
239287 tabScrollView. contentSize = CGSize ( width: originX - itemMargin, height: self . bounds. height)
240-
241288 tabScrollView. addSubview ( indicatorView)
242289
243- //如果item的长度小于self的width ,就重新计算margin排版
290+ //如果item的长度小于当前视图的width ,就重新计算margin排版
244291 if tabScrollView. contentSize. width < self . bounds. width {
245292 updateLabelsFrame ( )
246293 }
@@ -261,6 +308,23 @@ open class LCSlideMenu: UIView {
261308 indicatorView. layer. masksToBounds = true
262309 }
263310
311+ /// 配置遮罩视图
312+ private func configCoverView( ) {
313+
314+ tabScrollView. insertSubview ( coverView, at: 0 )
315+
316+ guard let titleLabel = itemsLabel. first else { return }
317+ let coverX : CGFloat = titleLabel. frame. origin. x
318+ let coverW : CGFloat = titleLabel. frame. width
319+ let coverH : CGFloat = coverHeight
320+ let coverY : CGFloat = ( titleLabel. frame. height - coverH) * 0.5 // 遮罩视图Y值
321+
322+ coverView. frame = CGRect ( x: coverX, y: coverY, width: coverW, height: coverH)
323+ coverView. layer. cornerRadius = coverHeight * 0.4 // 设置圆角
324+ coverView. layer. masksToBounds = true
325+ }
326+
327+
264328 /// 监听item点击事件
265329 ///
266330 /// - Parameter gesture: 手势
@@ -281,6 +345,8 @@ open class LCSlideMenu: UIView {
281345
282346 changeIndicatorViewPosition ( currentIndex, old: itemSelectedIndex)
283347
348+ changeCoverViewPosition ( currentIndex, old: itemSelectedIndex)
349+
284350 resetTabScrollViewContentOffset ( currentLabel)
285351
286352 resetMainScrollViewContentOffset ( itemSelectedIndex)
@@ -322,6 +388,28 @@ open class LCSlideMenu: UIView {
322388 self . indicatorView. frame = indicatorFrame
323389 }
324390 }
391+
392+
393+
394+ /// 改变coverView的位置
395+ ///
396+ /// - Parameters:
397+ /// - current: 当前标题索引
398+ /// - old: 之前标题索引
399+ private func changeCoverViewPosition( _ current: Int , old: Int ) {
400+
401+ // 获取之前label的尺寸
402+ let frame = itemsLabel [ old] . frame
403+
404+ /// 遮罩视图的Frame
405+ let coverFrame = CGRect ( x: frame. origin. x, y: coverView. frame. origin. y, width: frame. size. width, height: coverHeight)
406+
407+ // 动画改变 coverView 的位置
408+ UIView . animate ( withDuration: 0.25 ) {
409+ self . coverView. frame = coverFrame
410+ }
411+
412+ }
325413
326414 /// 当item过少时,更新itemLabel位置
327415 private func updateLabelsFrame( ) {
@@ -353,15 +441,18 @@ open class LCSlideMenu: UIView {
353441 case . normal: // 默认
354442 leftItem. textColor = progress <= 0.5 ? selectedColor : unSelectedColor
355443 rightItem. textColor = progress <= 0.5 ? unSelectedColor : selectedColor
356- default :
444+ case . cover: // 遮罩
445+ leftItem. textColor = averageColor ( currentColor: selectedColor, oldColor: unSelectedColor, percent: progress)
446+ rightItem. textColor = averageColor ( currentColor: unSelectedColor, oldColor: selectedColor, percent: progress)
447+ default : // .transfrom:放大
357448 if progress <= 0.5 { // 如果进度 < 0.5
358449 leftItem. textColor = selectedColor
359450 rightItem. textColor = unSelectedColor
360451 UIView . animate ( withDuration: 0.25 , animations: {
361452 leftItem. transform = CGAffineTransform ( scaleX: 1.2 , y: 1.2 )
362453 rightItem. transform = CGAffineTransform . identity
363454 } )
364- } else {
455+ } else {
365456 leftItem. textColor = unSelectedColor
366457 rightItem. textColor = selectedColor
367458 UIView . animate ( withDuration: 0.25 , animations: {
@@ -437,7 +528,7 @@ open class LCSlideMenu: UIView {
437528
438529 let leftItem = itemsLabel [ leftIndex]
439530 let rightItem = itemsLabel [ rightIndex]
440-
531+ /// 总得移动距离
441532 let totalSpace = rightItem. center. x - leftItem. center. x
442533 indicatorView. center = CGPoint ( x: leftItem. center. x + totalSpace * ratio, y: indicatorView. center. y)
443534 }
@@ -463,21 +554,80 @@ open class LCSlideMenu: UIView {
463554
464555 let leftItem = itemsLabel [ leftIndex]
465556 let rightItem = itemsLabel [ rightIndex]
557+
558+ var frame = self . indicatorView. frame
559+ let leftItemWidth : CGFloat = leftItem. frame. width
560+ let leftItemFrameMinX : CGFloat = leftItem. frame. minX
561+ let rightItemWidth : CGFloat = rightItem. frame. width
562+ let rightItemFrameMaxX : CGFloat = rightItem. frame. maxX
563+
466564 /// 间距
467565 let distance : CGFloat = indicatorType == . stretch ? 0 : indicatorAnimatePadding
468- var frame = self . indicatorView. frame
469- let maxWidth = rightItem. frame. maxX - leftItem. frame. minX - distance * 2
566+
567+ /// 最大宽度: 右边Item最大X值 - 左边Item最小X值 - 2倍间距
568+ let maxWidth = rightItemFrameMaxX - leftItemFrameMinX - distance * 2
569+
470570 if ratio <= 0.5 {
471- frame. size. width = leftItem . frame . width + ( maxWidth - leftItem . frame . width ) * ( ratio / 0.5 )
472- frame. origin. x = leftItem . frame . minX + distance * ( ratio / 0.5 )
571+ frame. size. width = leftItemWidth + ( maxWidth - leftItemWidth ) * ( ratio / 0.5 )
572+ frame. origin. x = leftItemFrameMinX + distance * ( ratio / 0.5 )
473573 } else {
474- frame. size. width = rightItem . frame . width + ( maxWidth - rightItem . frame . width ) * ( ( 1 - ratio) / 0.5 )
475- frame. origin. x = rightItem . frame . maxX - frame. size. width - distance * ( ( 1 - ratio) / 0.5 )
574+ frame. size. width = rightItemWidth + ( maxWidth - rightItemWidth ) * ( ( 1 - ratio) / 0.5 )
575+ frame. origin. x = rightItemFrameMaxX - frame. size. width - distance * ( ( 1 - ratio) / 0.5 )
476576 }
477577
478578 self . indicatorView. frame = frame
479579 }
480580
581+
582+ /// 处理cover状态
583+ ///
584+ /// - Parameter offsetX: 偏移量
585+ fileprivate func handleCoverType( _ offsetX: CGFloat ) {
586+
587+ if offsetX <= 0 {
588+ //左边界
589+ leftIndex = 0
590+ rightIndex = 0
591+ } else if offsetX >= mainScrollView. contentSize. width {
592+ //右边界
593+ leftIndex = itemsLabel. count - 1
594+ rightIndex = leftIndex
595+ } else {
596+ //中间
597+ leftIndex = Int ( offsetX / mainScrollView. bounds. width)
598+ rightIndex = leftIndex + 1
599+ }
600+ /// 比例
601+ let ratio = offsetX / mainScrollView. bounds. width - CGFloat( leftIndex)
602+ if ratio == 0 { return }
603+
604+ let leftItem = itemsLabel [ leftIndex]
605+ let rightItem = itemsLabel [ rightIndex]
606+
607+ var frame = self . coverView. frame
608+ let leftItemWidth : CGFloat = leftItem. frame. width
609+ let leftItemFrameMinX : CGFloat = leftItem. frame. minX
610+ let rightItemWidth : CGFloat = rightItem. frame. width
611+ let rightItemFrameMaxX : CGFloat = rightItem. frame. maxX
612+
613+ /// 间距
614+ let distance : CGFloat = titleStyle == . cover ? 0 : 8
615+
616+ /// 最大宽度: 右边Item最大X值 - 左边Item最小X值 - 2倍间距
617+ let maxWidth = rightItem. frame. maxX - leftItem. frame. minX - distance * 2
618+
619+ if ratio <= 0.5 {
620+ frame. size. width = leftItemWidth + ( maxWidth - leftItemWidth) * ( ratio / 0.5 )
621+ frame. origin. x = leftItemFrameMinX + distance * ( ratio / 0.5 )
622+ } else {
623+ frame. size. width = rightItemWidth + ( maxWidth - rightItemWidth) * ( ( 1 - ratio) / 0.5 )
624+ frame. origin. x = rightItemFrameMaxX - frame. size. width - distance * ( ( 1 - ratio) / 0.5 )
625+ }
626+ self . coverView. frame = frame
627+
628+ }
629+
630+
481631 /// 渐变颜色
482632 ///
483633 /// - Parameters:
@@ -517,7 +667,7 @@ extension LCSlideMenu: UIScrollViewDelegate {
517667
518668 // MARK: 当scrollview处于滚动状态时, offset发生改变,就会调用此函数. 直到停止.
519669 public func scrollViewDidScroll( _ scrollView: UIScrollView ) {
520-
670+
521671 /// 滚动视图横向(x)移动数值
522672 let offsetX = scrollView. contentOffset. x
523673 switch indicatorType {
@@ -529,6 +679,10 @@ extension LCSlideMenu: UIScrollViewDelegate {
529679 handleFollowTextIndicatorType ( offsetX)
530680 }
531681
682+ if titleStyle == . cover {
683+ handleCoverType ( offsetX)
684+ }
685+
532686 //计算偏移的相对位移
533687 let relativeLacation = mainScrollView. contentOffset. x / mainScrollView. frame. width - CGFloat( leftIndex)
534688 if relativeLacation == 0 { return }
0 commit comments