iOS自定义控件:分段选择器-Swift
2018-10-18 本文已影响52人
今晚月色
镇楼专用
废话不多说,直接上代码
1、数据源方法
// MARK: ============ SegmentSelectorManagerDataSource ============
@objc protocol SegmentSelectorManagerDataSource:NSObjectProtocol {
/// 主要配置 -> 必须要实现
func nameOfSliderItems(segemntControl:SegmentSelectorManager) -> Array<String>
func childViewControllers(segemntControl:SegmentSelectorManager) -> Array<UIViewController>
/// 字体颜色配置 -> 有默认
@objc optional func colorOfSlider(segemntControl:SegmentSelectorManager) -> UIColor
@objc optional func colorOfTopView(segemntControl:SegmentSelectorManager) -> UIColor
/// 背景颜色配置 -> 有默认
@objc optional func colorOfSliderItemsTitle(segemntControl:SegmentSelectorManager) -> UIColor
@objc optional func colorOfHighlightedSliderItemsTitle(segemntControl:SegmentSelectorManager) -> UIColor
/// 高度配置 -> 有默认
@objc optional func heightOfTopView(segemntControl:SegmentSelectorManager) -> CGFloat
@objc optional func heightOfSlider(segemntControl:SegmentSelectorManager) -> CGFloat
}
2、代理方法
// MARK: ============ SegmentSelectorManagerDelegate ============
protocol SegmentSelectorManagerDelegate {
func slideView(sliderView:SegmentSelectorManager, didSelectItemAtIndex:Int) -> Void
}
3、页面实现
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
/// 数据源
public weak var dataSource:SegmentSelectorManagerDataSource? {
didSet {
// 名称数组
namesOfSlideItems = dataSource?.nameOfSliderItems(segemntControl: self)
// 控制器数组
childControllersArray = dataSource?.childViewControllers(segemntControl: self)
if let ds = dataSource {
if (ds.responds(to: #selector(ds.colorOfHighlightedSliderItemsTitle(segemntControl:)))) {
//按钮字体颜色默认
colorOfSlideItemsTitle = ds.colorOfSliderItemsTitle!(segemntControl: self)
}
if (ds.responds(to: #selector(ds.colorOfHighlightedSliderItemsTitle(segemntControl:)))) {
// 按钮字体颜色选中
colorOfHighlightedSlideItemsTitle = ds.colorOfHighlightedSliderItemsTitle!(segemntControl: self)
}
if (ds.responds(to: #selector(ds.colorOfSlider(segemntControl:)))) {
// 指示器颜色
colorOfSlider = ds.colorOfSlider!(segemntControl: self)
}
if ds.responds(to: #selector(ds.heightOfTopView(segemntControl:))) {
// 顶部视图高度
heightOfTopView = (ds.heightOfTopView!(segemntControl: self))
}
if ds.responds(to: #selector(ds.heightOfTopView(segemntControl:))) {
// 指示器高度
heightOfSlider = (ds.heightOfSlider!(segemntControl: self))
}
}
}
}
// 代理
public var delegate:SegmentSelectorManagerDelegate?
var namesOfSlideItems: Array<String>? = [] // 名称数组
var colorOfSlider: UIColor? = UIColor.orange // 指示器颜色
var colorOfSlideView: UIColor? = UIColor.white // 顶部视图颜色
var colorOfSlideItemsTitle: UIColor? = UIColor.gray // 默认字体颜色
var colorOfHighlightedSlideItemsTitle: UIColor? = UIColor.orange // 选中字体颜色
var heightOfTopView:CGFloat = 45 // 顶部视图高度
var heightOfSlider:CGFloat = 2 // 指示器高度
let SliderThanSliderView_WidthRatio:CGFloat = 1 // 按钮和指示器宽度比
var buttonsArray:Array<UIButton>? = [] // 所有按钮的数组
var childControllersArray:Array<UIViewController>? = [] // 控制器数组
let SCREEN_WIDTH = UIScreen.main.bounds.size.width
let SCREEN_HEIGHT = UIScreen.main.bounds.height
/// 顶部View
lazy var slideBar:UIView = {
let view = UIView.init()
view.backgroundColor = colorOfSlideView
addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
view.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
view.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
view.topAnchor.constraint(equalTo: topAnchor).isActive = true
view.heightAnchor.constraint(equalToConstant: self.heightOfTopView).isActive = true
return view
}()
// 底部指示器
var slider:UIView?
/// 控制器ScrollView
lazy var contentScrollView:UIScrollView = {
let sc = UIScrollView.init()
sc.isDirectionalLockEnabled = true
sc.backgroundColor = UIColor.white
sc.isPagingEnabled = true
sc.showsHorizontalScrollIndicator = false
sc.delegate = self
sc.bounces = false
addSubview(sc)
sc.translatesAutoresizingMaskIntoConstraints = false
sc.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
sc.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
sc.topAnchor.constraint(equalTo: self.slideBar.bottomAnchor).isActive = true
sc.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
return sc
}()
4、绘制页面UI
// 获取当前页面的控制器
public func viewController()->UIViewController? {
var nextResponder: UIResponder? = self
repeat {
nextResponder = nextResponder?.next
if let viewController = nextResponder as? UIViewController {
return viewController
}
} while nextResponder != nil
return nil
}
override func layoutSubviews() {
super.layoutSubviews()
addSlider()
addButton()
addContentScrollView()
}
/// 添加按钮到顶部视图
func addButton() {
let numberOfItems:Int = (namesOfSlideItems?.count)!
let slideItemWidth = SCREEN_WIDTH / CGFloat(numberOfItems)
let sliderWidth = slideItemWidth * SliderThanSliderView_WidthRatio
let position_x = (slideItemWidth - sliderWidth) / 2.0
for index in 0..<numberOfItems {
let b = UIButton.init(type: .custom)
b.tag = index
b.setTitle(namesOfSlideItems![index], for: .normal)
b.setTitleColor(colorOfSlideItemsTitle, for: .normal)
b.titleLabel?.textAlignment = .center
b.addTarget(self, action: #selector(buttonTouched(button:)), for: .touchUpInside)
buttonsArray?.append(b)
b.frame = CGRect(x: position_x + slideItemWidth * CGFloat(index),
y: 5,
width: sliderWidth,
height: heightOfTopView-5)
slideBar.addSubview(b)
if index == 0 {
b.setTitleColor(colorOfHighlightedSlideItemsTitle!, for: .normal)
}
}
}
// 添加底部指示器到顶部视图
func addSlider() {
let slideItemWidth = SCREEN_WIDTH / CGFloat((namesOfSlideItems?.count)!)
let sliderWidth = slideItemWidth * SliderThanSliderView_WidthRatio
let position_x = (slideItemWidth - sliderWidth) / 2.0
slideBar.addSubview(sliderView(frame: CGRect(x: position_x,
y: heightOfTopView - heightOfSlider,
width: sliderWidth,
height: heightOfSlider)))
slideBar.backgroundColor = colorOfSlideView
slider?.backgroundColor = colorOfSlider
}
// 添加ScrollView
func addContentScrollView() {
contentScrollView.contentSize = CGSize(width: SCREEN_WIDTH * CGFloat((namesOfSlideItems?.count)!), height: 0)
for (index) in (childControllersArray?.enumerated())! {
index.element.view.frame = CGRect(x: CGFloat(index.offset) * SCREEN_WIDTH,
y: 0,
width: contentScrollView.frame.width,
height: contentScrollView.frame.height)
contentScrollView.addSubview(index.element.view)
viewController()?.addChild(index.element)
index.element.didMove(toParent: viewController())
}
}
// vc滚动动画
func animateSlider(tag:Int) -> Void {
contentScrollView.setContentOffset(CGPoint(x: SCREEN_WIDTH * CGFloat(tag), y: 0), animated: true)
}
// 指示器滚动动画
func animateSliderToPosition(offset:CGPoint) -> Void {
let slideItemWidth = SCREEN_WIDTH / CGFloat((namesOfSlideItems?.count)!)
let sliderWidth = slideItemWidth * SliderThanSliderView_WidthRatio
let position_x = (slideItemWidth - sliderWidth) / 2.0
let newFrame = CGRect(x: (offset.x / SCREEN_WIDTH) * slideItemWidth + position_x,
y: (slider?.frame.origin.y)!,
width: (slider?.frame.width)!,
height: (slider?.frame.height)!)
slider?.frame = newFrame
for (index) in (buttonsArray?.enumerated())! {
index.element.setTitleColor(colorOfSlideItemsTitle, for: .normal)
}
var buttonTag = 0
let ratio = offset.x / SCREEN_WIDTH
let tempRation = Int(ratio)
let decimal:CGFloat = ratio - CGFloat(tempRation)
if decimal >= 0.5 {
buttonTag = Int(ratio) + 1
} else {
buttonTag = Int(ratio)
}
buttonsArray![buttonTag].setTitleColor(colorOfHighlightedSlideItemsTitle, for: .normal)
}
/// 根据frame初始化指示器
func sliderView(frame:CGRect) -> UIView {
slider = UIView.init(frame: frame)
return slider!
}
/// 点击事件
@objc func buttonTouched(button:UIButton) {
delegate?.slideView(sliderView: self, didSelectItemAtIndex: button.tag)
animateSlider(tag: tag)
contentScrollView.setContentOffset(CGPoint(x: SCREEN_WIDTH * CGFloat(button.tag), y: 0), animated: true)
}
5、使用Extension实现UIScrollViewDidScrollDelegate
// MARK: ============ UIScrollViewDidScrollDelegate ============
extension SegmentSelectorManager:UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
animateSliderToPosition(offset: scrollView.contentOffset)
}
}
6、使用方法(实例代码)
override func setupSubViewsProperties() {
segmentView = SegmentSelectorManager.init(frame: CGRect.zero)
segmentView?.delegate = self
segmentView?.dataSource = self
view.addSubview(segmentView!)
}
override func setupSubViewsConstrains() {
if let seg = segmentView {
seg.translatesAutoresizingMaskIntoConstraints = false
seg.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
seg.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
seg.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -kTabBarHeight).isActive = true
seg.topAnchor.constraint(equalTo: wd_navgationBar_normal.bottomAnchor).isActive = true
}
}
extension HotPlayController: SegmentSelectorManagerDataSource {
func nameOfSliderItems(segemntControl: SegmentSelectorManager) -> Array<String> {
return ["正在热映", "即将上映"]
}
func childViewControllers(segemntControl: SegmentSelectorManager) -> Array<UIViewController> {
return [HotPlayingController(), HotWillPlayViewController()]
}
func heightOfTopView(segemntControl: SegmentSelectorManager) -> CGFloat {
return 45
}
func heightOfSlider(segemntControl: SegmentSelectorManager) -> CGFloat {
return 3
}
func colorOfSlider(segemntControl slider: SegmentSelectorManager) -> UIColor {
return UIColor.wd_init(r: 73, g: 73, b: 73)
}
func colorOfTopView(segemntControl: SegmentSelectorManager) -> UIColor {
return UIColor.white
}
func colorOfSliderItemsTitle(segemntControl: SegmentSelectorManager) -> UIColor {
return UIColor.gray
}
func colorOfHighlightedSliderItemsTitle(segemntControl: SegmentSelectorManager) -> UIColor {
return UIColor.wd_init(r: 73, g: 73, b: 73)
}
}
extension HotPlayController: SegmentSelectorManagerDelegate {
func slideView(sliderView: SegmentSelectorManager, didSelectItemAtIndex: Int) {
}
}