CustomIndicatorView
- Swift
- 目前只支持水平方向,垂直方向同理
- 需要使用RxSwift、RxCocoa、Snapkit
效果图
QQ20201115-225616-HD.gif
CustomIndicatorView 的全部代码
//
// CustomIndicatorView.swift
// IndicatorView
//
// Created by lam on 2020/11/15.
//
import Foundation
import SnapKit
import RxSwift
import RxCocoa
class CustomIndicatorView: UIView {
private let bag = DisposeBag()
private var bgView: UIView!
private var indicator: UIView!
private var indicatorWidth: CGFloat = 40
var autoIndicatorHidden: Bool = true
var autoIndicatorWidth: Bool = true
var indicatorMinWidth: CGFloat = 40
var indicatorColor: UIColor = .red {
didSet {
indicator.backgroundColor = indicatorColor
}
}
var bgViewColor: UIColor = .init(white: 0.2, alpha: 0.2) {
didSet {
bgView.backgroundColor = bgViewColor
}
}
var scrollView: UIScrollView? {
didSet {
observeScrollView()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
if frame.size.width == 0
|| frame.size.height == 0 {
fatalError("width and height is can't be 0")
}
self.backgroundColor = .clear
setupViews()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
func setupViews() {
bgView = UIView(frame: self.bounds)
bgView.backgroundColor = bgViewColor
self.addSubview(bgView)
bgView.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
indicator = UIView(frame: .zero)
indicator.backgroundColor = indicatorColor
bgView.addSubview(indicator)
indicator.snp.makeConstraints { (make) in
make.left.equalTo(0)
make.width.equalTo(indicatorWidth)
make.height.centerY.equalToSuperview()
}
bgView.layer.masksToBounds = true
bgView.layer.cornerRadius = self.bounds.height/2
indicator.layer.masksToBounds = true
indicator.layer.cornerRadius = self.bounds.height/2
}
func observeScrollView() {
guard let gScrollView = self.scrollView else { return }
/// observe scrollView’s contentSize.
/// change indicatorWidth
gScrollView.rx.observe(CGSize.self, "contentSize")
.subscribe { [unowned self] (event: Event<CGSize?>) in
guard let aa = event.element,
let size = aa,
size != .zero
else
{ return }
/// auto indicator width
if autoIndicatorWidth {
var width = self.frame.size.width / (size.width / gScrollView.frame.size.width)
if width < self.indicatorMinWidth {
width = self.indicatorMinWidth
}
self.indicator.snp.updateConstraints { (make) in
make.width.equalTo(width)
}
self.indicatorWidth = width
}
/// hidden if need
if autoIndicatorHidden {
self.isHidden = (size.width == gScrollView.frame.size.width)
}
}.disposed(by: bag)
/// observe scrollView's contentOffset.
scrollView?.rx.contentOffset.subscribe({ (event: Event<CGPoint>) in
guard let point = event.element, point != .zero else { return }
/// indicator's x
var indicatorX = point.x * (self.bounds.width - self.indicatorWidth)/(gScrollView.contentSize.width - gScrollView.bounds.width)
if indicatorX < -self.indicatorWidth/2 {
indicatorX = -self.indicatorWidth/2
} else if indicatorX > self.bounds.size.width - self.indicatorWidth/2 {
indicatorX = self.bounds.size.width - self.indicatorWidth/2
}
self.indicator.snp.updateConstraints { (make) in
make.left.equalTo(indicatorX)
}
}).disposed(by: bag)
}
}
怎么用?
let indicatorSize = CGSize(width: 100, height: 5)
indicatorView = CustomIndicatorView(frame: CGRect(origin: .zero, size: indicatorSize))
indicatorView.indicatorColor = .magenta
indicatorView.scrollView = scrollView
scrollView.contentSize = CGSize(width: 544, height: 0)
self.view.addSubview(indicatorView)
indicatorView.snp.makeConstraints { (make) in
make.size.equalTo(indicatorSize)
make.centerX.equalToSuperview()
make.bottom.equalTo(scrollView.snp_bottom).inset(5)
}