引言
自定义封装了一个Segment,使用简单,下面是效果图。
代码部分
- 首先定义一个SegmentStyle,把我们需要的一些属性都放进去,如是否显示下划线,是否显示遮罩等等,这样在我们封装好后可以更加方便我们的使用。
public struct SegmentStyle{
/// 是否显示遮盖
public var showCover = false
/// 是否显示下划线
public var showLine = false
/// 是否缩放文字
public var scaleTitle = false
/// 是否可以滚动标题
public var scrollTitle = true
/// 下面的滚动条的高度 默认2
public var scrollLineHeight: CGFloat = 2
/// 下面的滚动条的颜色
public var scrollLineColor = UIColor.brown
/// 遮盖的背景颜色
public var coverBackgroundColor = UIColor.lightGray
/// 遮盖圆角
public var coverCornerRadius: CGFloat = 10.0
/// cover的高度 默认28
public var coverHeight: CGFloat = 28.0
/// 文字间的间隔 默认15
public var titleMargin: CGFloat = 15
/// 文字 字体 默认14.0
public var titleFont = UIFont.systemFont(ofSize: 14.0)
/// 放大倍数 默认1.3
public var titleBigScale: CGFloat = 1.1
/// 默认倍数 不可修改
let titleOriginalScale: CGFloat = 1.0
/// 文字正常状态颜色 请使用RGB空间的颜色值!! 如果提供的不是RGB空间的颜色值就可能crash
public var normalTitleColor = UIColor(red: 51.0/255.0, green: 53.0/255.0, blue: 75/255.0, alpha: 1.0)
/// 文字选中状态颜色 请使用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,重新创建一个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)
// 根据Titles添加相应的控件
setupTitles()
// 设置Frame
setupUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
把需要的属性加进去
open var segmentStyle: SegmentStyle
/// 点击响应
open var titleBtnOnClick:((_ label: UILabel, _ index: Int)->Void)?
/// 所有标题的宽度
fileprivate var titleWidthArry: [CGFloat] = []
/// 所有的标题
fileprivate var titles: [String]
/// 缓存标题
fileprivate var labelsArray: [UILabel] = []
/// self.bounds.size.width
fileprivate var currentWidth: CGFloat = 0
/// 记录当前选中的下标
fileprivate var currentIndex = 0
/// 记录上一个下标
fileprivate var oldIndex = 0
/// 所以文字的总宽度
fileprivate var labelWithMax: CGFloat = 0
/// 遮罩x和文字x的间隙
fileprivate var xGap = 5
/// 遮罩宽度比文字宽度多的部分
fileprivate var wGap: Int {
return 2 * xGap
}
- 懒加载一个ScrollView作为容器
/// 管理标题的滚动
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
}()
- 是否显示滚动条或者遮罩
/// 是否显示滚动条
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
}()
- 基本的东西都设置好了,现在我们要添加相应的item
// 根据Titles添加相应的控件
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
// 添加点击手势
let tapGes = UITapGestureRecognizer(target: self, action: #selector(self.titleLabelOnClick(_:)))
label.addGestureRecognizer(tapGes)
// 计算文本宽高
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)
}
}
// 设置Frame
fileprivate func setupUI() {
// 设置Label位置
currentWidth = bounds.size.width
setUpLabelsPosition()
// 设置滚动条和遮罩
setupScrollLineAndCover()
if segmentStyle.scrollTitle{
if let lastLabel = labelsArray.last {
scrollView.contentSize = CGSize(width: lastLabel.frame.maxX + segmentStyle.titleMargin, height: 0)
}
}
}
/// 设置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{
// 计算标题长度总和
for (index, labelWith) in titleWidthArry.enumerated(){
labelWithMax += labelWith + 2 * segmentStyle.titleMargin
}
// 当标题的长度总和没有屏幕宽度长时,平分屏幕宽度
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)
}
}
// 当标题的长度总和比屏幕宽度短时
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 {
// 缩放, 设置初始的label的transform
if segmentStyle.scaleTitle {
firstLabel.currentTransformSx = segmentStyle.titleBigScale
}
// 设置初始状态文字的颜色
firstLabel.textColor = segmentStyle.selectedTitleColor
}
}
/// 设置滚动条和遮罩
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
// 设置遮罩位置
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)
}
// 设置滚动条位置
scrollLine?.frame = CGRect(x: coverX, y: bounds.size.height - segmentStyle.scrollLineHeight, width: coverW, height: segmentStyle.scrollLineHeight)
}
- 当有多个标题时,可以让选中的标题居中,这样可以方便使用。
/// 让选中标签居中显示
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需要移动的偏移量
var offSetX = currentLabel.center.x - currentWidth/2
if offSetX < 0 {
offSetX = 0
}
/// scrollView最大偏移量
var maxOffSetX = scrollView.contentSize.width - currentWidth
// 可以滚动的区域小余屏幕宽度
if maxOffSetX < 0 {
maxOffSetX = 0
}
// 当offSetX偏移量大于最大偏移量时,就直接等于最大偏移量,否则会出现最后一个标签也居中显示
if offSetX > maxOffSetX {
offSetX = maxOffSetX
}
// 设置scrollView的偏移量
scrollView.setContentOffset(CGPoint(x:offSetX, y: 0), animated: true)
}
- 实现Label手势点击方法
@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地址。希望能够帮助到一些需要的人。自己也可以尝试多种组合。