Вы можете сделать это с помощью UIStackView
, используя виды "spacer".
Добавьте четкое значение UIView
между каждым видом "точка" и ограничьте ширину каждого вида "spacer", равную first представление "spacer".
Добавьте UIStackView
, ограничьте его ширину и центрY линией отслеживания и установите для свойств:
Axis: Horizontal
Alignment: Fill
Distribution: Fill
Spacing: 0
Ваш код для добавления «точек» будет выглядеть примерно так:
for i in 0..<numberOfDots {
create a dot view
add it to the stackView using .addArrangedSubview()
one fewer spacers than dots (e.g. 4 dots have a spacer between each = 3 spacers), so,
if this is NOT the last dot,
create a spacer view
add it to the stackView
}
Отслеживайте виды спейсеров и устанавливайте их ограничения по ширине, каждый равный первому виду спейсеров.
Вот некоторый стартовый код, который может помочь вам начать работу.Комментарии должны прояснить, что делается.Все выполняется в коде (без @IBOutlet
s), поэтому вы можете запустить его, добавив контроллер представления в раскадровку и назначив его пользовательский класс для DotsViewController
.Он добавляет представление как «нормальное» подпредставление ... но, конечно, также может быть добавлено как подпредставление ячейки.
class DotView: UIView {
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = bounds.size.height * 0.5
}
}
class TrackingLineView: UIView {
var theTrackingLine: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
return v
}()
var theStack: UIStackView = {
let v = UIStackView()
v.translatesAutoresizingMaskIntoConstraints = false
v.axis = .horizontal
v.alignment = .fill
v.distribution = .fill
v.spacing = 0
return v
}()
var trackingDot: DotView = {
let v = DotView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = UIColor(red: 0.0, green: 0.5, blue: 1.0, alpha: 1.0)
return v
}()
let dotWidth = CGFloat(6)
let trackingDotWidth = CGFloat(20)
var trackingDotCenterX = NSLayoutConstraint()
var dotViews = [DotView]()
var trackingPosition: Int = 0 {
didSet {
let theDot = dotViews[trackingPosition]
trackingDotCenterX.isActive = false
trackingDotCenterX = trackingDot.centerXAnchor.constraint(equalTo: theDot.centerXAnchor, constant: 0.0)
trackingDotCenterX.isActive = true
}
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
// add the tracking line
addSubview(theTrackingLine)
// add the "big" tracking dot
addSubview(trackingDot)
// add the stack view that will hold the small dots (and spacers)
addSubview(theStack)
// the "big" tracking dot will be positioned behind a small dot, so we need to
// keep a reference to its centerXAnchor constraint
trackingDotCenterX = trackingDot.centerXAnchor.constraint(equalTo: theTrackingLine.centerXAnchor, constant: 0.0)
NSLayoutConstraint.activate([
theTrackingLine.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0.0),
theTrackingLine.centerYAnchor.constraint(equalTo: centerYAnchor, constant: 0.0),
theTrackingLine.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 1.0, constant: -20.0),
theTrackingLine.heightAnchor.constraint(equalToConstant: 2.0),
theStack.centerXAnchor.constraint(equalTo: theTrackingLine.centerXAnchor, constant: 0.0),
theStack.centerYAnchor.constraint(equalTo: theTrackingLine.centerYAnchor, constant: 0.0),
theStack.widthAnchor.constraint(equalTo: theTrackingLine.widthAnchor, multiplier: 1.0, constant: 0.0),
trackingDotCenterX,
trackingDot.widthAnchor.constraint(equalToConstant: trackingDotWidth),
trackingDot.heightAnchor.constraint(equalTo: trackingDot.widthAnchor, multiplier: 1.0),
trackingDot.centerYAnchor.constraint(equalTo: theTrackingLine.centerYAnchor, constant: 0.0),
])
}
func setDots(with colors: [UIColor]) -> Void {
// remove any previous dots and spacers
// (in case we're changing the number of dots after creating the view)
theStack.arrangedSubviews.forEach {
$0.removeFromSuperview()
}
// reset the array of dot views
// (in case we're changing the number of dots after creating the view)
dotViews = [DotView]()
// we're going to set all spacer views to equal widths, so use
// this var to hold a reference to the first one we create
var firstSpacer: UIView?
colors.forEach {
c in
// create a DotView
let v = DotView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = c
// add to array so we can reference it later
dotViews.append(v)
// add it to the stack view
theStack.addArrangedSubview(v)
// dots are round (equal width to height)
NSLayoutConstraint.activate([
v.widthAnchor.constraint(equalToConstant: dotWidth),
v.heightAnchor.constraint(equalTo: v.widthAnchor, multiplier: 1.0),
])
// we use 1 fewer spacers than dots, so if this is not the last dot
if c != colors.last {
// create a spacer (clear view)
let s = UIView()
s.translatesAutoresizingMaskIntoConstraints = false
s.backgroundColor = .clear
// add it to the stack view
theStack.addArrangedSubview(s)
if firstSpacer == nil {
firstSpacer = s
} else {
// we know it's not nil, but we have to unwrap it anyway
if let fs = firstSpacer {
NSLayoutConstraint.activate([
s.widthAnchor.constraint(equalTo: fs.widthAnchor, multiplier: 1.0),
])
}
}
}
}
}
}
class DotsViewController: UIViewController {
var theButton: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .red
v.setTitle("Move Tracking Dot", for: .normal)
v.setTitleColor(.white, for: .normal)
return v
}()
var theTrackingLineView: TrackingLineView = {
let v = TrackingLineView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .white
return v
}()
var trackingDots: [UIColor] = [
.yellow,
.red,
.orange,
.green,
.purple,
]
var currentTrackingPosition = 0
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(red: 1.0, green: 0.8, blue: 0.5, alpha: 1.0)
view.addSubview(theTrackingLineView)
NSLayoutConstraint.activate([
theTrackingLineView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0.0),
theTrackingLineView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0.0),
theTrackingLineView.heightAnchor.constraint(equalToConstant: 100.0),
theTrackingLineView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.9),
])
theTrackingLineView.setDots(with: trackingDots)
theTrackingLineView.trackingPosition = currentTrackingPosition
// add a button so we can move the tracking dot
view.addSubview(theButton)
NSLayoutConstraint.activate([
theButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 40.0),
theButton.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0.0),
])
theButton.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
}
@objc func buttonTapped(_ sender: Any) -> Void {
// if we're at the last dot, reset to 0
if currentTrackingPosition < trackingDots.count - 1 {
currentTrackingPosition += 1
} else {
currentTrackingPosition = 0
}
theTrackingLineView.trackingPosition = currentTrackingPosition
UIView.animate(withDuration: 0.25, animations: {
self.view.layoutIfNeeded()
})
}
}
Результат: