Improving Stack (adding orientation and spacing)
This commit is contained in:
parent
03999cb555
commit
ad082723d6
44
Sources/YadUI/Extensions/UIView/UIView+Constraints.swift
Normal file
44
Sources/YadUI/Extensions/UIView/UIView+Constraints.swift
Normal file
@ -0,0 +1,44 @@
|
||||
//
|
||||
// UIView+Constraints.swift
|
||||
//
|
||||
//
|
||||
// Created by Selim Mustafaev on 28.07.2023.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
|
||||
public func pin(to view: UIView, insets: UIEdgeInsets = .zero) {
|
||||
NSLayoutConstraint.activate([
|
||||
leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: insets.left),
|
||||
trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -insets.right),
|
||||
topAnchor.constraint(equalTo: view.topAnchor, constant: insets.top),
|
||||
bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -insets.bottom)
|
||||
])
|
||||
}
|
||||
|
||||
public func pin(toSafeArea view: UIView, insets: UIEdgeInsets = .zero) {
|
||||
NSLayoutConstraint.activate([
|
||||
leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: insets.left),
|
||||
trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -insets.right),
|
||||
topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: insets.top),
|
||||
bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -insets.bottom)
|
||||
])
|
||||
}
|
||||
|
||||
public func constraint(src: UIView,
|
||||
srcAttr: NSLayoutConstraint.Attribute,
|
||||
dst: UIView,
|
||||
dstAttr: NSLayoutConstraint.Attribute,
|
||||
constant: CGFloat = 0) {
|
||||
|
||||
addConstraint(.init(item: src,
|
||||
attribute: srcAttr,
|
||||
relatedBy: .equal,
|
||||
toItem: dst,
|
||||
attribute: dstAttr,
|
||||
multiplier: 1.0,
|
||||
constant: constant))
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
//
|
||||
// UIView.swift
|
||||
// UIView+Fluent.swift
|
||||
//
|
||||
//
|
||||
// Created by Мустафаев Селим Мустафаевич on 27.07.2023.
|
||||
// Created by Selim Mustafaev on 28.07.2023.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
@ -14,15 +14,6 @@ extension UIView {
|
||||
return self
|
||||
}
|
||||
|
||||
public func pin(to view: UIView, insets: UIEdgeInsets = .zero) {
|
||||
NSLayoutConstraint.activate([
|
||||
leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: insets.left),
|
||||
trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -insets.right),
|
||||
topAnchor.constraint(equalTo: view.topAnchor, constant: insets.top),
|
||||
bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -insets.bottom)
|
||||
])
|
||||
}
|
||||
|
||||
public func width(_ width: CGFloat) -> Self {
|
||||
widthAnchor.constraint(equalToConstant: width).isActive = true
|
||||
return self
|
||||
46
Sources/YadUI/Views/Stack/Stack+Directional.swift
Normal file
46
Sources/YadUI/Views/Stack/Stack+Directional.swift
Normal file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// Stack+Directional.swift
|
||||
//
|
||||
//
|
||||
// Created by Selim Mustafaev on 28.07.2023.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public class VStack: Stack {
|
||||
|
||||
public init(alignment: Alignment = .fill,
|
||||
distribution: Distribution = .fill,
|
||||
spacing: CGFloat = 0,
|
||||
@StackViewBuilder _ content: () -> [UIView]) {
|
||||
|
||||
super.init(axis: .vertical,
|
||||
alignment: alignment,
|
||||
distribution: distribution,
|
||||
spacing: spacing,
|
||||
content)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
public class HStack: Stack {
|
||||
|
||||
public init(alignment: Alignment = .fill,
|
||||
distribution: Distribution = .fill,
|
||||
spacing: CGFloat = 0,
|
||||
@StackViewBuilder _ content: () -> [UIView]) {
|
||||
|
||||
super.init(axis: .horizontal,
|
||||
alignment: alignment,
|
||||
distribution: distribution,
|
||||
spacing: spacing,
|
||||
content)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
27
Sources/YadUI/Views/Stack/Stack+Enums.swift
Normal file
27
Sources/YadUI/Views/Stack/Stack+Enums.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Stack+Enums.swift
|
||||
//
|
||||
//
|
||||
// Created by Selim Mustafaev on 28.07.2023.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension Stack {
|
||||
|
||||
public enum Alignment {
|
||||
|
||||
case fill
|
||||
}
|
||||
|
||||
public enum Distribution {
|
||||
|
||||
case fill
|
||||
}
|
||||
|
||||
public enum Axis {
|
||||
|
||||
case horizontal
|
||||
case vertical
|
||||
}
|
||||
}
|
||||
106
Sources/YadUI/Views/Stack/Stack.swift
Normal file
106
Sources/YadUI/Views/Stack/Stack.swift
Normal file
@ -0,0 +1,106 @@
|
||||
//
|
||||
// Stack.swift
|
||||
//
|
||||
//
|
||||
// Created by Мустафаев Селим Мустафаевич on 27.07.2023.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public class Stack: UIView {
|
||||
|
||||
private var arrangedSubviews: [UIView]
|
||||
private var alignment: Alignment
|
||||
private var distribution: Distribution
|
||||
private var axis: Axis
|
||||
private var spacing: CGFloat
|
||||
|
||||
var aStart: NSLayoutConstraint.Attribute {
|
||||
axis == .vertical ? .leading : .top
|
||||
}
|
||||
|
||||
var aEnd: NSLayoutConstraint.Attribute {
|
||||
axis == .vertical ? .trailing : .bottom
|
||||
}
|
||||
|
||||
var dStart: NSLayoutConstraint.Attribute {
|
||||
axis == .vertical ? .top : .leading
|
||||
}
|
||||
|
||||
var dEnd: NSLayoutConstraint.Attribute {
|
||||
axis == .vertical ? .bottom : .trailing
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public init(axis: Axis = .vertical,
|
||||
alignment: Alignment = .fill,
|
||||
distribution: Distribution = .fill,
|
||||
spacing: CGFloat = 0,
|
||||
@StackViewBuilder _ content: () -> [UIView]) {
|
||||
|
||||
self.axis = axis
|
||||
self.alignment = alignment
|
||||
self.distribution = distribution
|
||||
self.spacing = spacing
|
||||
self.arrangedSubviews = content()
|
||||
super.init(frame: .zero)
|
||||
|
||||
self.arrangedSubviews.forEach(addSubview)
|
||||
addAlignmentConstraints()
|
||||
addDistributionConstraints()
|
||||
}
|
||||
|
||||
func addAlignmentConstraints() {
|
||||
|
||||
switch alignment {
|
||||
case .fill:
|
||||
addAlignmentFillConstraints()
|
||||
}
|
||||
}
|
||||
|
||||
func addDistributionConstraints() {
|
||||
|
||||
switch distribution {
|
||||
case .fill:
|
||||
addDistributionFillConstraints()
|
||||
}
|
||||
}
|
||||
|
||||
func addAlignmentFillConstraints() {
|
||||
guard let firstView = arrangedSubviews.first else { return }
|
||||
|
||||
let otherViews = arrangedSubviews.dropFirst()
|
||||
|
||||
constraint(src: firstView, srcAttr: aStart, dst: self, dstAttr: aStart)
|
||||
constraint(src: firstView, srcAttr: aEnd, dst: self, dstAttr: aEnd)
|
||||
|
||||
for view in otherViews {
|
||||
constraint(src: view, srcAttr: aStart, dst: firstView, dstAttr: aStart)
|
||||
constraint(src: view, srcAttr: aEnd, dst: firstView, dstAttr: aEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func addDistributionFillConstraints() {
|
||||
guard let firstView = arrangedSubviews.first,
|
||||
let lastView = arrangedSubviews.last
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
let middleViews = arrangedSubviews.dropFirst().dropLast()
|
||||
|
||||
constraint(src: firstView, srcAttr: dStart, dst: self, dstAttr: dStart)
|
||||
constraint(src: lastView, srcAttr: dEnd, dst: self, dstAttr: dEnd)
|
||||
|
||||
var currentView = firstView
|
||||
for view in middleViews {
|
||||
constraint(src: view, srcAttr: dStart, dst: currentView, dstAttr: dEnd, constant: spacing)
|
||||
currentView = view
|
||||
}
|
||||
|
||||
constraint(src: lastView, srcAttr: dStart, dst: currentView, dstAttr: dEnd, constant: spacing)
|
||||
}
|
||||
}
|
||||
@ -1,115 +0,0 @@
|
||||
//
|
||||
// VStack.swift
|
||||
//
|
||||
//
|
||||
// Created by Мустафаев Селим Мустафаевич on 27.07.2023.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public enum StackAlignment {
|
||||
|
||||
case fill
|
||||
}
|
||||
|
||||
public enum StackDistribution {
|
||||
|
||||
case fill
|
||||
}
|
||||
|
||||
public enum StackAxis {
|
||||
|
||||
case horizontal
|
||||
case vertical
|
||||
}
|
||||
|
||||
public final class VStack: UIView {
|
||||
|
||||
private var arrangedSubviews: [UIView]
|
||||
private var alignment: StackAlignment
|
||||
private var distribution: StackDistribution
|
||||
private var axis: StackAxis
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public init(axis: StackAxis = .vertical,
|
||||
alignment: StackAlignment = .fill,
|
||||
distribution: StackDistribution = .fill,
|
||||
@StackViewBuilder _ content: () -> [UIView]) {
|
||||
|
||||
self.axis = axis
|
||||
self.alignment = alignment
|
||||
self.distribution = distribution
|
||||
self.arrangedSubviews = content()
|
||||
super.init(frame: .zero)
|
||||
|
||||
self.arrangedSubviews.forEach(addSubview)
|
||||
NSLayoutConstraint.activate(generateAlignmentConstraints())
|
||||
NSLayoutConstraint.activate(generateDistributionConstraints())
|
||||
}
|
||||
|
||||
func generateAlignmentConstraints() -> [NSLayoutConstraint] {
|
||||
|
||||
switch alignment {
|
||||
case .fill:
|
||||
return generateAlignmentFillConstraints()
|
||||
}
|
||||
}
|
||||
|
||||
func generateDistributionConstraints() -> [NSLayoutConstraint] {
|
||||
|
||||
switch distribution {
|
||||
case .fill:
|
||||
return generateDistributionFillConstraints()
|
||||
}
|
||||
}
|
||||
|
||||
func generateAlignmentFillConstraints() -> [NSLayoutConstraint] {
|
||||
guard let firstView = arrangedSubviews.first else {
|
||||
return []
|
||||
}
|
||||
|
||||
let otherViews = arrangedSubviews.dropFirst()
|
||||
|
||||
var constraints: [NSLayoutConstraint] = [
|
||||
firstView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
firstView.trailingAnchor.constraint(equalTo: trailingAnchor)
|
||||
]
|
||||
|
||||
for view in otherViews {
|
||||
constraints.append(contentsOf: [
|
||||
view.leadingAnchor.constraint(equalTo: firstView.leadingAnchor),
|
||||
view.trailingAnchor.constraint(equalTo: firstView.trailingAnchor)
|
||||
])
|
||||
}
|
||||
|
||||
return constraints
|
||||
}
|
||||
|
||||
func generateDistributionFillConstraints() -> [NSLayoutConstraint] {
|
||||
guard let firstView = arrangedSubviews.first,
|
||||
let lastView = arrangedSubviews.last
|
||||
else {
|
||||
return []
|
||||
}
|
||||
|
||||
let middleViews = arrangedSubviews.dropFirst().dropLast()
|
||||
|
||||
var constraints: [NSLayoutConstraint] = [
|
||||
firstView.topAnchor.constraint(equalTo: topAnchor),
|
||||
lastView.bottomAnchor.constraint(equalTo: bottomAnchor)
|
||||
]
|
||||
|
||||
var currentView = firstView
|
||||
for view in middleViews {
|
||||
constraints.append(view.topAnchor.constraint(equalTo: currentView.bottomAnchor))
|
||||
currentView = view
|
||||
}
|
||||
|
||||
constraints.append(lastView.topAnchor.constraint(equalTo: currentView.bottomAnchor))
|
||||
|
||||
return constraints
|
||||
}
|
||||
}
|
||||
@ -15,16 +15,18 @@ class ViewController: UIViewController {
|
||||
|
||||
let child = buildView()
|
||||
view.addSubview(child)
|
||||
child.pin(to: view)
|
||||
child.pin(toSafeArea: view)
|
||||
}
|
||||
|
||||
@RootViewBuilder func buildView() -> UIView {
|
||||
VStack {
|
||||
VStack(spacing: 16) {
|
||||
UIView()
|
||||
.background(.red)
|
||||
//.width(100)
|
||||
.height(100)
|
||||
UIView()
|
||||
.background(.green)
|
||||
//.width(100)
|
||||
.height(100)
|
||||
UIView()
|
||||
.background(.blue)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user