Несмотря на то, что UIKit постепенно уступает место более современному во всех отношениях SwiftUI, до сих пор остается огромное количество проектов, которые основаны на фреймворке UIKit. Далее будет представлена библиотека, которая позволяет выполнять конструирование внешнего вида намного быстрее и удобнее, чем стандартные средства.

Реализация

В сети интернет можно найти огромное количество решений на данную тему, как от известных и солидных компаний, так и от отдельных разработчиков. Тем не менее, на мой взгляд, все существующие решения перегружены так называемым синтаксическим сахаром. Например, нет никакой необходимости в использовании closure в библиотеке SpanKit. Или объявлять дополнительный инициализатор для UIView в PureLayout.

По этой причине было разработано более простое и легковесное решение для создания constraints.

import UIKit

protocol LayoutItem {
	func constraint(_ attribute: NSLayoutConstraint.Attribute,
					_ relation: NSLayoutConstraint.Relation,
					to target: LayoutItem?,
					_ targetAttribute: NSLayoutConstraint.Attribute?,
					multiplier: CGFloat,
					constant: CGFloat) -> NSLayoutConstraint
}

extension LayoutItem {
	func constraint(_ attribute: NSLayoutConstraint.Attribute,
					_ relation: NSLayoutConstraint.Relation = .equal,
					to target: LayoutItem? = nil,
					_ targetAttribute: NSLayoutConstraint.Attribute? = nil,
					multiplier: CGFloat = 1,
					constant: CGFloat = 0) -> NSLayoutConstraint {
		return NSLayoutConstraint(
			item: self,
			attribute: attribute,
			relatedBy: relation,
			toItem: target,
			attribute: targetAttribute ?? attribute,
			multiplier: multiplier,
			constant: constant
		)
	}
}

// MARK: - UIView+LayoutItem

extension UIView: LayoutItem {}

// MARK: - UILayoutGuide+LayoutItem

extension UILayoutGuide: LayoutItem {}

Такое решение было выбрано в пользу стандартной библиотеки, а если быть точнее, то синтезированы на основе методов constraint(equalTo:) и подобных для NSLayoutAnchor. И с учетом уклона swift в сторону протокольно-ориентированного программирования.

Таким образом верстка экрана выглядит следующим образом.

NSLayoutConstraint.activate([
			scrollView.constraint(.top, to: view, .top),
			scrollView.constraint(.leading, to: view, .leading),
			scrollView.constraint(.bottom, to: view, .bottom),
			scrollView.constraint(.trailing, to: view, .trailing),
			
			scrollView.contentLayoutGuide.constraint(.width, to: view),
			
			stackView.constraint(.top, to: scrollView.contentLayoutGuide, .top),
			stackView.constraint(.leading, .greaterThanOrEqual, to: scrollView.contentLayoutGuide, .leading),
			stackView.constraint(.leading, to: view, .leadingMargin),
			stackView.constraint(.bottom, to: scrollView.contentLayoutGuide, .bottom),
			stackView.constraint(.trailing, .lessThanOrEqual, to: scrollView.contentLayoutGuide, .trailing),
			stackView.constraint(.trailing, to: view, .trailingMargin)
		])

Внимательный читатель может отметить, как правильно верстать UIScrollView с системными отступами для каждого устройства без создания вложенного containerView.

Заключение

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

Ссылки

  1. SnapKit

  2. PureLayout

  3. Stevia

Комментарии (1)


  1. classx
    30.05.2023 04:59

    почти в всех проектах я использовал TinyConstraints и был очень доволен

    а что на счет производительности?