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
|
import UIKit
|
||||||
@ -14,15 +14,6 @@ extension UIView {
|
|||||||
return self
|
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 {
|
public func width(_ width: CGFloat) -> Self {
|
||||||
widthAnchor.constraint(equalToConstant: width).isActive = true
|
widthAnchor.constraint(equalToConstant: width).isActive = true
|
||||||
return self
|
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()
|
let child = buildView()
|
||||||
view.addSubview(child)
|
view.addSubview(child)
|
||||||
child.pin(to: view)
|
child.pin(toSafeArea: view)
|
||||||
}
|
}
|
||||||
|
|
||||||
@RootViewBuilder func buildView() -> UIView {
|
@RootViewBuilder func buildView() -> UIView {
|
||||||
VStack {
|
VStack(spacing: 16) {
|
||||||
UIView()
|
UIView()
|
||||||
.background(.red)
|
.background(.red)
|
||||||
|
//.width(100)
|
||||||
.height(100)
|
.height(100)
|
||||||
UIView()
|
UIView()
|
||||||
.background(.green)
|
.background(.green)
|
||||||
|
//.width(100)
|
||||||
.height(100)
|
.height(100)
|
||||||
UIView()
|
UIView()
|
||||||
.background(.blue)
|
.background(.blue)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user