引言
自定義封裝了一個(gè)Segment,使用簡單,下面是效果圖。
自定義Segment效果圖.gif
代碼部分
- 首先定義一個(gè)SegmentStyle棚唆,把我們需要的一些屬性都放進(jìn)去来惧,如是否顯示下劃線冗栗,是否顯示遮罩等等,這樣在我們封裝好后可以更加方便我們的使用供搀。
public struct SegmentStyle{
/// 是否顯示遮蓋
public var showCover = false
/// 是否顯示下劃線
public var showLine = false
/// 是否縮放文字
public var scaleTitle = false
/// 是否可以滾動(dòng)標(biāo)題
public var scrollTitle = true
/// 下面的滾動(dòng)條的高度 默認(rèn)2
public var scrollLineHeight: CGFloat = 2
/// 下面的滾動(dòng)條的顏色
public var scrollLineColor = UIColor.brown
/// 遮蓋的背景顏色
public var coverBackgroundColor = UIColor.lightGray
/// 遮蓋圓角
public var coverCornerRadius: CGFloat = 10.0
/// cover的高度 默認(rèn)28
public var coverHeight: CGFloat = 28.0
/// 文字間的間隔 默認(rèn)15
public var titleMargin: CGFloat = 15
/// 文字 字體 默認(rèn)14.0
public var titleFont = UIFont.systemFont(ofSize: 14.0)
/// 放大倍數(shù) 默認(rèn)1.3
public var titleBigScale: CGFloat = 1.1
/// 默認(rèn)倍數(shù) 不可修改
let titleOriginalScale: CGFloat = 1.0
/// 文字正常狀態(tài)顏色 請使用RGB空間的顏色值!! 如果提供的不是RGB空間的顏色值就可能crash
public var normalTitleColor = UIColor(red: 51.0/255.0, green: 53.0/255.0, blue: 75/255.0, alpha: 1.0)
/// 文字選中狀態(tài)顏色 請使用RGB空間的顏色值!! 如果提供的不是RGB空間的顏色值就可能crash
public var selectedTitleColor = UIColor(red: 255.0/255.0, green: 0.0/255.0, blue: 121/255.0, alpha: 1.0)
public init() {
}
- 自定義SegmentView隅居,重新創(chuàng)建一個(gè)Swift文件,初始化
import UIKit
class ce: UIView {
public init(frame: CGRect, segmentStyle: SegmentStyle, titles: [String]) {
self.segmentStyle = segmentStyle
self.titles = titles
super.init(frame: frame)
addSubview(scrollView)
// 根據(jù)Titles添加相應(yīng)的控件
setupTitles()
// 設(shè)置Frame
setupUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
把需要的屬性加進(jìn)去
open var segmentStyle: SegmentStyle
/// 點(diǎn)擊響應(yīng)
open var titleBtnOnClick:((_ label: UILabel, _ index: Int)->Void)?
/// 所有標(biāo)題的寬度
fileprivate var titleWidthArry: [CGFloat] = []
/// 所有的標(biāo)題
fileprivate var titles: [String]
/// 緩存標(biāo)題
fileprivate var labelsArray: [UILabel] = []
/// self.bounds.size.width
fileprivate var currentWidth: CGFloat = 0
/// 記錄當(dāng)前選中的下標(biāo)
fileprivate var currentIndex = 0
/// 記錄上一個(gè)下標(biāo)
fileprivate var oldIndex = 0
/// 所以文字的總寬度
fileprivate var labelWithMax: CGFloat = 0
/// 遮罩x和文字x的間隙
fileprivate var xGap = 5
/// 遮罩寬度比文字寬度多的部分
fileprivate var wGap: Int {
return 2 * xGap
}
- 懶加載一個(gè)ScrollView作為容器
/// 管理標(biāo)題的滾動(dòng)
fileprivate lazy var scrollView: UIScrollView = {
let scrollV = UIScrollView()
scrollV.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
scrollV.showsHorizontalScrollIndicator = false
scrollV.bounces = true
scrollV.isPagingEnabled = false
scrollV.scrollsToTop = false
return scrollV
}()
- 是否顯示滾動(dòng)條或者遮罩
/// 是否顯示滾動(dòng)條
fileprivate lazy var scrollLine: UIView? = {[unowned self] in
let line = UIView()
return self.segmentStyle.showLine ? line : nil
}()
/// 是否顯示遮罩
fileprivate lazy var coverView: UIView? = {[unowned self] in
let cover = UIView()
cover.layer.cornerRadius = CGFloat(self.segmentStyle.coverCornerRadius)
cover.layer.masksToBounds = true
return self.segmentStyle.showCover ? cover : nil
}()
- 基本的東西都設(shè)置好了葛虐,現(xiàn)在我們要添加相應(yīng)的item
// 根據(jù)Titles添加相應(yīng)的控件
fileprivate func setupTitles() {
for (index, title) in titles.enumerated(){
let label = CustomLabel(frame: CGRect.zero)
label.tag = index
label.text = title
label.font = segmentStyle.titleFont
label.textColor = UIColor.black
label.textAlignment = .center
label.isUserInteractionEnabled = true
// 添加點(diǎn)擊手勢
let tapGes = UITapGestureRecognizer(target: self, action: #selector(self.titleLabelOnClick(_:)))
label.addGestureRecognizer(tapGes)
// 計(jì)算文本寬高
let size = (title as NSString).boundingRect(with: CGSize(width: CGFloat(MAXFLOAT), height: 0.0), options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: label.font], context: nil)
// 緩存文字寬度
titleWidthArry.append(size.width)
// 緩存label
labelsArray.append(label)
// 添加label
scrollView.addSubview(label)
}
}
// 設(shè)置Frame
fileprivate func setupUI() {
// 設(shè)置Label位置
currentWidth = bounds.size.width
setUpLabelsPosition()
// 設(shè)置滾動(dòng)條和遮罩
setupScrollLineAndCover()
if segmentStyle.scrollTitle{
if let lastLabel = labelsArray.last {
scrollView.contentSize = CGSize(width: lastLabel.frame.maxX + segmentStyle.titleMargin, height: 0)
}
}
}
/// 設(shè)置label的位置
fileprivate func setUpLabelsPosition() {
var titleX: CGFloat = 0.0
let titleY: CGFloat = 0.0
var titleW: CGFloat = 0.0
let titleH = bounds.size.height - segmentStyle.scrollLineHeight
if !segmentStyle.scrollTitle{
titleW = currentWidth/CGFloat(titles.count)
for(index, label) in labelsArray.enumerated(){
titleX = titleW * CGFloat(index)
label.frame = CGRect(x: titleX, y: titleY, width: titleW, height: titleH)
}
}else{
// 計(jì)算標(biāo)題長度總和
for (index, labelWith) in titleWidthArry.enumerated(){
labelWithMax += labelWith + 2 * segmentStyle.titleMargin
}
// 當(dāng)標(biāo)題的長度總和沒有屏幕寬度長時(shí)胎源,平分屏幕寬度
if labelWithMax <= currentWidth{
for(index, label) in labelsArray.enumerated(){
let currWidth = currentWidth - 2 * segmentStyle.titleMargin
titleW = currWidth/CGFloat(labelsArray.count)
titleX = segmentStyle.titleMargin
if index != 0{
let lastLabel = labelsArray[index - 1]
titleX = lastLabel.frame.maxX
}
label.frame = CGRect(x: titleX, y: titleY, width: titleW, height: titleH)
}
}
// 當(dāng)標(biāo)題的長度總和比屏幕寬度短時(shí)
else{
for(index, label) in labelsArray.enumerated(){
titleW = titleWidthArry[index]
titleX = segmentStyle.titleMargin
if index != 0{
let lastLabel = labelsArray[index - 1]
titleX = lastLabel.frame.maxX + segmentStyle.titleMargin * 2
}
label.frame = CGRect(x: titleX, y: titleY, width: titleW, height: titleH)
}
}
}
if let firstLabel = labelsArray[0] as? CustomLabel {
// 縮放, 設(shè)置初始的label的transform
if segmentStyle.scaleTitle {
firstLabel.currentTransformSx = segmentStyle.titleBigScale
}
// 設(shè)置初始狀態(tài)文字的顏色
firstLabel.textColor = segmentStyle.selectedTitleColor
}
}
/// 設(shè)置滾動(dòng)條和遮罩
fileprivate func setupScrollLineAndCover(){
if let line = scrollLine {
line.backgroundColor = segmentStyle.scrollLineColor
scrollView.addSubview(line)
}
if let cover = coverView {
cover.backgroundColor = segmentStyle.coverBackgroundColor
scrollView.insertSubview(cover, at: 0)
}
let coverX = labelsArray[0].frame.origin.x
let coverW = labelsArray[0].frame.size.width
let coverH: CGFloat = segmentStyle.coverHeight
let coverY = (bounds.size.height - coverH) / 2
// 設(shè)置遮罩位置
if segmentStyle.scrollTitle {
// 這里x-xGap width+wGap 是為了讓遮蓋的左右邊緣和文字有一定的距離
coverView?.frame = CGRect(x: coverX - CGFloat(xGap), y: coverY, width: coverW + CGFloat(wGap), height: coverH)
} else {
coverView?.frame = CGRect(x: coverX, y: coverY, width: coverW, height: coverH)
}
// 設(shè)置滾動(dòng)條位置
scrollLine?.frame = CGRect(x: coverX, y: bounds.size.height - segmentStyle.scrollLineHeight, width: coverW, height: segmentStyle.scrollLineHeight)
}
- 當(dāng)有多個(gè)標(biāo)題時(shí),可以讓選中的標(biāo)題居中屿脐,這樣可以方便使用涕蚤。
/// 讓選中標(biāo)簽居中顯示
public func adjustTitleOffSetToCurrentIndex(_ currentIndex: Int){
let currentLabel = labelsArray[currentIndex]
for index in labelsArray.enumerated(){
if index.offset != currentIndex{
index.element.textColor = self.segmentStyle.normalTitleColor
}
}
/// scrollView需要移動(dòng)的偏移量
var offSetX = currentLabel.center.x - currentWidth/2
if offSetX < 0 {
offSetX = 0
}
/// scrollView最大偏移量
var maxOffSetX = scrollView.contentSize.width - currentWidth
// 可以滾動(dòng)的區(qū)域小余屏幕寬度
if maxOffSetX < 0 {
maxOffSetX = 0
}
// 當(dāng)offSetX偏移量大于最大偏移量時(shí),就直接等于最大偏移量的诵,否則會(huì)出現(xiàn)最后一個(gè)標(biāo)簽也居中顯示
if offSetX > maxOffSetX {
offSetX = maxOffSetX
}
// 設(shè)置scrollView的偏移量
scrollView.setContentOffset(CGPoint(x:offSetX, y: 0), animated: true)
}
- 實(shí)現(xiàn)Label手勢點(diǎn)擊方法
@objc func titleLabelOnClick(_ tapGes: UITapGestureRecognizer){
guard let currentLabel = tapGes.view as? CustomLabel else { return }
currentIndex = currentLabel.tag
print(currentLabel.tag)
adjustUIWhenBtnOnClickWithAnimate(true)
}
使用部分
var style = SegmentStyle()
style.scrollTitle = true
style.showLine = true
style.scrollLineColor = UIColor.blue
let scrollview = ScrollSegmentView(frame: CGRect(x: 0, y: 64, width: self.view.frame.width, height: 40), segmentStyle: style, titles: ["絕地求生","絕地大逃殺"])
self.view.addSubview(scrollview)
好了万栅,最后附上Demo的GItHub地址。希望能夠幫助到一些需要的人西疤。自己也可以嘗試多種組合烦粒。