Тут можно найти реализацию готового проекта
На сегодняшний день во многих приложениях мы можем наблюдать stepper, большинство из них кастомные. Несмотря на то, что Apple предоставляет уже реализацию готового степпера, иногда он не подходит по разным причинам. Это пример подхода к реализации кастомного степпера для кофейни.
Есть разные способы в достижение цели, но сегодня я вам покажу тот, которые не не нашел. В этой статье мы будем использовать верстку кодом, stackView с 2 кнопками (-,+) и лейбл.
Для того, чтобы многократно использовать наш степпер, сделаем его классом, который наследуется от UIView. По сути это полноценный UI-компонент, который можно будет потом взять в свои проекты.
Самое важное - логика, которая будет обрабатывать текущее значения нашего степпера. Для этого будет использовать переменную, которая будет отвечать за текущее значения степпера и обновлять текст лейбла.
final class CustomStepper: UIView {
private lazy var currentValue = 1
}
Дальше нам нужно создать 2 кнопки и лейбл, из которых и будет состоять наш степпер. Для того, чтобы отслеживать состояние степпера, мы будем использовать enum, который будет управлять состояниями кнопок. Мы использовали теги, для чтобы не делать 2 метода для обработки нажатия кнопок.
private enum ButtonState: Int, CaseIterable {
case decrease = 0
case increase
}
private lazy var decreaseButton: UIButton = {
let button = UIButton()
button.tag = ButtonState.decrease.rawValue
button.setTitleColor(.black, for: .normal)
button.setTitle("-", for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
return button
}()
private lazy var currentStepValueLabel: UILabel = {
let label = UILabel()
label.textColor = .black
label.text = "\(currentValue)"
label.font = .systemFont(ofSize: 15)
return label
}()
private lazy var increaseButton: UIButton = {
let button = UIButton()
button.tag = ButtonState.increase.rawValue
button.setTitle("+", for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
button.setTitleColor(.black, for: .normal)
return button
}()
Имея все необходимые элементы, мы можем реализовать метод, который отвечает за логику нашего степпера и привязать его к нашим кнопкам.
//MARK: - Actions
@objc private func buttonAction(_ sender: UIButton) {
let buttonState = ButtonState(rawValue: sender.tag)
switch buttonState {
case .decrease:
currentValue = currentValue > 1 ? currentValue - 1 : currentValue
case .increase:
currentValue += 1
default:
return
}
currentStepValueLabel.text = "\(currentValue)"
}
Будем использовать паттерн делегирования, чтобы передать данные степпера в наш контроллер для дальнейшей логики проекта и обновления значения.
protocol CustomStepperOutput: AnyObject {
func customStepper(_ didChangeValue: Int)
}
protocol CustomStepperInput: AnyObject {
func update(_ value:Int)
}
Это внутренний интерфейс, через который мы можем проинициализировать счетчик (при необходимости)
//MARK: - CustomStepperInput
extension CustomStepper: CustomStepperInput {
func update(_ value: Int) {
currentValue = value
}
}
Внутри контроллера проинициализируем наш степпер. Контроллер подпишем под делегатом степпера, поэтому контроллер будет реализовывать метод делегата, где получать данные от степпера. С этими данными контроллер может дальше осуществлять логику.
import UIKit
final class MainVC: UIViewController {
private lazy var stepperView = CustomStepper()
//MARK: - Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupConstraints()
}
//MARK: - Private
private func setupViews() {
view.backgroundColor = .white
view.addSubview(stepperView)
stepperView.delegate = self
}
private func setupConstraints() {
stepperView.snp.makeConstraints { make in
make.centerX.centerY.equalToSuperview()
}
}
}
//MARK: - CustomStepperOutput
extension MainVC: CustomStepperOutput {
func customStepper(_ didChangeValue: Int) {
print(didChangeValue)
}
}
Готово! Вот конечный результат: