Создание компонентного вью SensorView

от автора

Когда я занимался разработкой своего последнего проекта, я столкнулся с задачей создания удобного и интуитивно понятного интерфейса для отображения и переключения данных с физических датчиков по анализу качества окружающей среды. В этой статье я использую в качестве примеров параметры «Качество воздуха» и «УФ-индекс». Я не буду вдаваться в подробности работы с MapKit и маркерами, но пишу об этом для введения в контекст.

Предполагается, что с помощью этого компонента можно будет переключать значения, чтобы отображать разные параметры на маркерах карты. Я назвал компонент SensorView. Ниже я предоставлю код элемента целиком, но также оставляю ссылку на репозиторий проекта.

Мне был нужен аналог контекстного меню, но ТЗ требовало, чтобы появление опций появлялось не в отдельном нативном вью. Это должно было работать так: при нажатии на кнопку она разворачивается вниз, предлагая выбрать параметр или нажать на инфокнопку «What is this».

В этой статье я расскажу о том, как я создал компонентный вью с помощью UIKit в коде без использования Сториборда. Вот какие компоненты я использовал:

Переменные:

  • Константы для размеров: Здесь мы определяем размеры, которые будем использовать для размещения и стилизации наших элементов интерфейса.

  • Основная и второстепенная UIStackView: Эти элементы используются для группировки и вертикального размещения кнопок. Стеки было удобно использовать, потому что при параметре .isHidden = true у кнопок (кнопки скрыты) StackView автоматически сворачивается.

  • Кнопки: Здесь мы создаём основную кнопку, которая отображает текущий параметр, а также отвечает за активацию меню, кнопку для отображения качества воздуха, кнопку для отображения УФ-индекса, информационную кнопку и круглую кнопку для отображения пиктограммы текущего параметра. Сейчас она не отрабатывает событий.

Функции:

  • setupViews(): Эта функция отвечает за добавление всех элементов интерфейса на главное представление и их базовую настройку.

  • setupConstraints(): Здесь мы устанавливаем ограничения для всех элементов интерфейса, определяя их положение и размеры относительно друг друга и главного представления.

  • createButton(...): Это универсальная функция для создания кнопок с различными свойствами, что позволяет избежать дублирования кода при создании каждой новой кнопки.

Отработка событий:

  • Нажатие на основную кнопку: Функция mainButtonTapped() вызывается, когда пользователь нажимает на основную кнопку, что приводит к разворачиванию или сворачиванию меню.

  • Нажатие на кнопку качества воздуха: Функция airQualityButtonTapped() вызывается, когда пользователь выбирает параметр «Качество воздуха».

  • Нажатие на кнопку УФ-индекса: Функция uvIndexButtonTapped() активируется при выборе параметра «УФ-индекс».

Вот код этого элемента с моими комментариями:

import UIKit import SnapKit  // Перечисление для определения текущего параметра enum Parameter { case airQuality case uvIndex }  // Основной класс представления датчика class SensorView: UIView { // Структура для определения размеров и констант макета struct Layout {     static let screenWidth = UIScreen.main.bounds.width }  // Текущий выбранный параметр var currentParameter: Parameter = .airQuality  // Основной вертикальный stack view let primaryStackView: UIStackView = {     let stack = UIStackView()     stack.backgroundColor = .white     stack.axis = .vertical     stack.layer.cornerRadius = 26     let insets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 15)     stack.layoutMargins = insets     stack.isLayoutMarginsRelativeArrangement = true          return stack }()  // Второстепенный вертикальный stack view let secondaryStackView: UIStackView = {     let stack = UIStackView()     stack.backgroundColor = .white     stack.axis = .vertical     let insets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)     stack.layoutMargins = insets     stack.isLayoutMarginsRelativeArrangement = true     return stack }()  // Главная кнопка, показывающая текущий параметр private lazy var mainButton: UIButton = {     let button: UIButton = createButton(title: "Air Quality".uppercased(), action: #selector(mainButtonTapped), height: 58, target: self)     button.setImage(UIImage.init(systemName: "chevron.down"), for: .normal)     button.contentHorizontalAlignment = .left     button.imageEdgeInsets = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: -10)     button.titleEdgeInsets = UIEdgeInsets(top: 0, left: 40, bottom: 0, right: 0)     return button }()  // Кнопка для выбора качества воздуха private lazy var airQualityButton: UIButton = {     let button = createButton(title: "Air Quality", action: #selector(airQualityButtonTapped), height: 45, target: self)     button.isHidden = true     button.contentHorizontalAlignment = .leading     button.setImage(UIImage.init(systemName: "wind"), for: .normal)     button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 50, bottom: 0, right: 0)     button.imageEdgeInsets = UIEdgeInsets(top: 0, left: -10, bottom: 0, right: 0)     return button }()  // Кнопка для выбора индекса ультрафиолетового излучения private lazy var uvIndexButton: UIButton = {     let button = createButton(title: "UV index", action: #selector(uvIndexButtonTapped), height: 45, target: self)     button.isHidden = true     button.contentHorizontalAlignment = .leading     button.setImage(UIImage.init(systemName: "sun.max.fill"), for: .normal)     button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 50, bottom: 0, right: 0)     button.imageEdgeInsets = UIEdgeInsets(top: 0, left: -10, bottom: 0, right: 0)     return button }()  // Кнопка для вывода дополнительной информации private lazy var infoButton: UIButton = {     let button = createButton(title: "What is this?", action: #selector(mainButtonTapped), height: 58, target: self)     button.isHidden = true          if let currentTitle = button.title(for: .normal) {         let attributes: [NSAttributedString.Key: Any] = [             .underlineStyle: NSUnderlineStyle.single.rawValue         ]                  let attributedTitle = NSAttributedString(string: currentTitle, attributes: attributes)         button.setAttributedTitle(attributedTitle, for: .normal)     }          return button }()  // Круглая кнопка для визуализации текущего параметра private lazy var roundButton: UIButton = {     let button = UIButton()     let roundButtonDiameter: CGFloat = 75     button.backgroundColor = .cyan     button.layer.cornerRadius = roundButtonDiameter / 2     button.widthAnchor.constraint(equalToConstant: roundButtonDiameter).isActive = true     button.heightAnchor.constraint(equalToConstant: roundButtonDiameter).isActive = true     button.setImage(UIImage.init(systemName: "wind"), for: .normal)     return button }()  // Инициализатор init() {     super.init(frame: .zero)          setupViews()     setupConstraints() }  required init?(coder: NSCoder) {     fatalError("init(coder:) has not been implemented") }  // Метод для настройки представлений func setupViews() {     self.addSubview(primaryStackView)     primaryStackView.addArrangedSubview(mainButton)     primaryStackView.addArrangedSubview(secondaryStackView)     secondaryStackView.addArrangedSubview(airQualityButton)     secondaryStackView.addArrangedSubview(uvIndexButton)     primaryStackView.addArrangedSubview(infoButton)     self.addSubview(roundButton)      }  // Метод для настройки ограничений func setupConstraints() {     primaryStackView.snp.makeConstraints { make in         make.top.bottom.right.left.equalToSuperview().inset(30)     }          roundButton.snp.makeConstraints { make in         make.centerY.equalTo(mainButton)         make.right.equalTo(primaryStackView).offset(20)     } }  // Метод для создания кнопок, чтобы не копировать код private func createButton(title: String, action: Selector, height: CGFloat, target: Any) -> UIButton {     let button = UIButton()     button.setTitle(title, for: .normal)     button.setTitleColor(.black, for: .normal)     button.backgroundColor = .clear     button.layer.shadowColor = UIColor.black.cgColor     button.layer.shadowOffset = CGSize(width: 0, height: 2)     button.layer.shadowOpacity = 0.2     button.layer.shadowRadius = 4     button.addTarget(target, action: action, for: .touchUpInside)     button.widthAnchor.constraint(equalToConstant: Layout.screenWidth * 0.6).isActive = true     button.heightAnchor.constraint(equalToConstant: height).isActive = true     return button }  // Действие при нажатии на главную кнопку @objc private func mainButtonTapped() {     let areButtonsHidden = airQualityButton.isHidden          airQualityButton.isHidden.toggle()     uvIndexButton.isHidden.toggle()     infoButton.isHidden.toggle()          let image = areButtonsHidden ? UIImage(systemName: "chevron.up") : UIImage(systemName: "chevron.down")     mainButton.setImage(image, for: .normal) }  // Действие при нажатии на кнопку качества воздуха @objc private func airQualityButtonTapped() {     mainButton.setTitle("Air Quality".uppercased(), for: .normal)     airQualityButton.isHidden = true     uvIndexButton.isHidden = true     infoButton.isHidden = true     roundButton.setImage(UIImage.init(systemName: "wind"), for: .normal)     mainButton.setImage(UIImage.init(systemName: "chevron.down"), for: .normal) }  // Действие при нажатии на кнопку индекса ультрафиолетового излучения @objc private func uvIndexButtonTapped() {     mainButton.setTitle("UV index".uppercased(), for: .normal)     airQualityButton.isHidden = true     uvIndexButton.isHidden = true     infoButton.isHidden = true     roundButton.setImage(UIImage.init(systemName: "sun.max.fill"), for: .normal)     mainButton.setImage(UIImage.init(systemName: "chevron.down"), for: .normal) } }


ссылка на оригинал статьи https://habr.com/ru/articles/755742/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *