Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions Demo/Demo/HVStackDemoViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,13 @@ class HVStackDemoViewController: UIViewController {
// Support Spacer (Inspired by SwiftUI)
Spacer()

VStackView {
VStackView(distribution: .spacing(4)) {
UILabel().stack.then { label in
label.font = .systemFont(ofSize: 14, weight: .semibold)
label.textColor = .systemGreen
label.text = "H/VStack in UIKit"
}
Spacer(length: 4)
Divider(color: UIColor.blue)
Spacer(length: 12)
UILabel().stack.then { label in
label.font = .systemFont(ofSize: 12, weight: .regular)
label.textColor = .gray
Expand All @@ -42,7 +40,7 @@ class HVStackDemoViewController: UIViewController {

Spacer()

VStackView(distribution: .fillWidth(spacing: 10)) {
VStackView(distribution: .fillWidth(spacing: 4)) {

// view.stack.then (Inspired by Then [ https://github.com/devxoul/Then ])
UILabel().stack.then { label in
Expand Down Expand Up @@ -91,18 +89,14 @@ class HVStackDemoViewController: UIViewController {
}
}

HStackView(alignment: .top, distribution: .spacing(14)) {
Spacer(length: 12)

HStackView(alignment: .top, distribution: .spacing(14), padding: UIEdgeInsets(top: 2, left: 12, bottom: 2, right: 6)) {
VStackView {
Spacer(length: 6)
UIView().stack.size(6).then {
$0.backgroundColor = .systemBlue
$0.layer.cornerRadius = 3
$0.clipsToBounds = true
}
}

Spacer(length: 4)
UILabel().stack.then {
$0.textColor = .darkText
Expand Down
20 changes: 10 additions & 10 deletions Demo/Demo/HVStackWrapperViewDemoViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ let contentWidth: CGFloat = UIScreen.main.bounds.width - 32

class HVStackWrapperViewDemoViewController: UIViewController {

let content = VStackView {
let content = VStackView(distribution: .fillWidth(spacing: 0), padding: UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16)) {
HStackView() {
VStackView(alignment: .left) {
UILabel().stack.then {
Expand Down Expand Up @@ -44,7 +44,7 @@ class HVStackWrapperViewDemoViewController: UIViewController {
}

// wrapper in other view, need set width and sizeToFit
}.stack.width(contentWidth).sizeToFit(.width)
}

Spacer(length: 20)

Expand Down Expand Up @@ -86,7 +86,7 @@ class HVStackWrapperViewDemoViewController: UIViewController {
}
}
}
}.stack.width(contentWidth).sizeToFit(.width)
}

Spacer(length: 30)
HStackView(alignment: .bottom) {
Expand All @@ -101,11 +101,11 @@ class HVStackWrapperViewDemoViewController: UIViewController {
$0.font = .systemFont(ofSize: 18, weight: .medium)
$0.textColor = .blue
}
}.stack.width(contentWidth).sizeToFit(.width)
}.stack.sizeToFit(.width)

Spacer(length: 20)
VStackView(alignment: .left) {

VStackView(distribution: .fillWidth()) {
HStackView {
UIView().stack.size(40).then {
$0.backgroundColor = .systemPink
Expand All @@ -130,7 +130,7 @@ class HVStackWrapperViewDemoViewController: UIViewController {
$0.textColor = .black
$0.font = .systemFont(ofSize: 18, weight: .medium)
}
}.stack.width(contentWidth).sizeToFit(.width)
}
}
}

Expand All @@ -141,7 +141,7 @@ class HVStackWrapperViewDemoViewController: UIViewController {


let textLayer = CATextLayer()
textLayer.string = "This is CATextLayer in VStackLayerWrapperView2"
textLayer.string = "This is CATextLayer in VStackLayerContainerView"
textLayer.fontSize = 12
textLayer.foregroundColor = UIColor.black.cgColor
textLayer.contentsScale = UIScreen.main.scale
Expand All @@ -152,14 +152,14 @@ class HVStackWrapperViewDemoViewController: UIViewController {
textLayer
}
}

content.sizeToFit()
self.view.addSubview(content)
}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

content.pin.top(view.pin.safeArea).horizontally(16).sizeToFit()
content.pin.top(view.pin.safeArea).horizontally().sizeToFit(.width)
}

/*
Expand Down
17 changes: 0 additions & 17 deletions Demo/Demo/WrapStackDemoViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,6 @@ class WrapStackDemoViewController: UIViewController {

// Do any additional setup after loading the view.

let vStack = VStackView(distribution: .fillWidth(spacing: 10)) {
UILabel().stack.then {
$0.text = "StackKit"
$0.font = .systemFont(ofSize: 18, weight: .semibold)
$0.textColor = .black
}
Divider()
UILabel().stack.then {
$0.text = "Version 1.0.0"
$0.font = .systemFont(ofSize: 14, weight: .regular)
$0.textColor = .gray
}
}
vStack.sizeToFit()
vStack.frame.origin = CGPoint(x: 20, y: 120)

view.addSubview(vStack)
}

/*
Expand Down
70 changes: 16 additions & 54 deletions Sources/StackKit/HStackView.swift
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import UIKit

open class HStackView: UIView {
open class HStackView: UIView, StackView {

public var alignment: HStackAlignment = .center
public var distribution: HStackDistribution = .autoSpacing
public var padding: UIEdgeInsets = .zero

public required init(
alignment: HStackAlignment = .center,
distribution: HStackDistribution = .spacing(2),
padding: UIEdgeInsets = .zero,
@_StackKitHStackContentResultBuilder content: () -> [UIView] = { [] }
) {
super.init(frame: .zero)

self.alignment = alignment
self.distribution = distribution
self.padding = padding

for v in content() {
addSubview(v)
Expand Down Expand Up @@ -75,7 +78,7 @@ open class HStackView: UIView {
let w = effectiveSubviews.map({ $0.bounds }).reduce(CGRect.zero) { result, rect in
result.union(rect)
}.height
return CGSize(width: h, height: w)
return CGSize(width: h + paddingRight, height: w + paddingVertically)
}

open func hideIfNoEffectiveViews() {
Expand All @@ -95,11 +98,11 @@ open class HStackView: UIView {

switch alignment {
case .top:
effectiveSubviews.forEach { $0.frame.origin.y = 0 }
effectiveSubviews.forEach { $0.frame.origin.y = _stackContentRect.minY }
case .center:
effectiveSubviews.forEach { $0.center.y = frame.height / 2 }
effectiveSubviews.forEach { $0.center.y = _stackContentRect.midY }
case .bottom:
effectiveSubviews.forEach { $0.frame.origin.y = frame.height - $0.frame.height }
effectiveSubviews.forEach { $0.frame.origin.y = _stackContentRect.maxY - $0.frame.height }
}

switch distribution {
Expand Down Expand Up @@ -153,26 +156,6 @@ open class HStackView: UIView {
}
}

extension HStackView {

private func spacerViews() -> [SpacerView] {
effectiveSubviews.compactMap({ $0 as? SpacerView })
}
private func dynamicSpacerViews() -> [SpacerView] {
effectiveSubviews.compactMap({ $0 as? SpacerView }).filter({ $0.length == .greatestFiniteMagnitude })
}
private func dividerViews() -> [DividerView] {
effectiveSubviews.compactMap({ $0 as? DividerView })
}

private func viewsWithoutSpacer() -> [UIView] {
effectiveSubviews.filter({ ($0 as? SpacerView) == nil })
}
private func viewsWithoutSpacerAndDivider() -> [UIView] {
effectiveSubviews.filter({ ($0 as? SpacerView) == nil && ($0 as? DividerView) == nil })
}
}

extension HStackView {

/// 自动间距
Expand All @@ -185,9 +168,9 @@ extension HStackView {
///
private func autoSpacing() -> CGFloat {
let unspacerViews = viewsWithoutSpacer()
let spacersCount = spacerViews().map({ isSpacerBetweenViews($0) }).filter({ $0 }).count
let spacersCount = spacerViews().map({ isSpacerBetweenInTwoViews(spacerView: $0) }).filter({ $0 }).count
let number = unspacerViews.count - spacersCount - 1
return (frame.width - viewsWidth() - spacerSpecifyLength()) / CGFloat(max(1, number))
return (frame.width - viewsWidth() - lengthOfAllFixedLengthSpacer()) / CGFloat(max(1, number))
}

private func viewsWidth() -> CGFloat {
Expand All @@ -197,7 +180,7 @@ extension HStackView {
private func makeSpacing(_ spacing: CGFloat) {
for (index, subview) in effectiveSubviews.enumerated() {
if index == 0 {
subview.frame.origin.x = 0
subview.frame.origin.x = _stackContentRect.minX
} else {
let previousView = effectiveSubviews[index - 1]
if (previousView as? SpacerView) != nil || (subview as? SpacerView) != nil { // spacer and view no spacing
Expand All @@ -215,18 +198,18 @@ extension HStackView {
}
for subview in effectiveSubviews {
let oldHeight = subview.frame.height
subview.frame.size.height = frame.height
subview.frame.size.height = _stackContentWidth

// fix #https://github.com/iWECon/StackKit/issues/21
guard alignment == .center else {
continue
}
subview.frame.origin.y -= (frame.height - oldHeight) / 2
subview.frame.origin.y -= (_stackContentWidth - oldHeight) / 2
}
}

private func fillWidth() {
let maxW = frame.width - spacerSpecifyLength() - dividerSpecifyLength()
let maxW = frame.width - lengthOfAllFixedLengthSpacer() - dividerSpecifyLength()
var w = (maxW) / CGFloat(viewsWithoutSpacerAndDivider().count)

let unspacersView = viewsWithoutSpacerAndDivider()
Expand Down Expand Up @@ -265,27 +248,6 @@ extension HStackView {
// MARK: Spacer
extension HStackView {

// 取出固定 length 的 spacer
private func spacerSpecifyLength() -> CGFloat {
spacerViews()
.map({ $0.setLength })
.reduce(0, +)
}

private func isSpacerBetweenViews(_ spacer: SpacerView) -> Bool {
guard let index = effectiveSubviews.firstIndex(of: spacer) else {
return false
}

guard effectiveSubviews.count >= 3 else {
return false
}

let start: Int = 1
let end: Int = effectiveSubviews.count - 2
return (start ... end).contains(index)
}

private func fillSpecifySpacer() {
let spacers = effectiveSubviews.compactMap({ $0 as? SpacerView })
for spacer in spacers {
Expand All @@ -302,7 +264,7 @@ extension HStackView {
guard unspacerViews.count != effectiveSubviews.count else { return }

// 在 view 与 view 之间的 spacer view 数量: 两个 view 夹一个 spacer view
let betweenInViewsCount = spacerViews().map({ isSpacerBetweenViews($0) }).filter({ $0 }).count
let betweenInViewsCount = spacerViews().map({ isSpacerBetweenInTwoViews(spacerView: $0) }).filter({ $0 }).count
// 非 spacer view 的总宽度
let unspacerViewsWidth = viewsWidth()
// 排除 spacer view 后的间距
Expand All @@ -329,7 +291,7 @@ extension HStackView {

// 非 spacerView 的所有宽度
let unspacerViewsMaxWidth = unspacerViewsWidth + unspacerViewsSpacing
let spacersWidth = (frame.width - unspacerViewsMaxWidth - self.spacerSpecifyLength())
let spacersWidth = (frame.width - unspacerViewsMaxWidth - self.lengthOfAllFixedLengthSpacer())
let spacerWidth = spacersWidth / CGFloat(self.dynamicSpacerViews().count)

let spacerViews = self.spacerViews()
Expand Down
8 changes: 4 additions & 4 deletions Sources/StackKit/Layer+LayerWraperView/VStackLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ extension VStackLayer {
let unspacerViews = viewsWithoutSpacer()
let spacersCount = spacerLayers().map({ isSpacerBetweenViews($0) }).filter({ $0 }).count
let number = unspacerViews.count - spacersCount - 1
return Swift.max(0, (frame.height - viewsHeight() - spacerSpecifyLength()) / CGFloat(max(1, number)))
return Swift.max(0, (frame.height - viewsHeight() - lengthOfAllFixedLengthSpacer()) / CGFloat(max(1, number)))
}

private func viewsHeight() -> CGFloat {
Expand Down Expand Up @@ -176,7 +176,7 @@ extension VStackLayer {
///
/// 填充高度, 所有视图(排除 spacer)高度一致
private func fillHeight() {
let maxH = frame.height - spacerSpecifyLength() - dividerSpecifyLength()
let maxH = frame.height - lengthOfAllFixedLengthSpacer() - dividerSpecifyLength()
var h = (maxH) / CGFloat(viewsWithoutSpacerAndDivider().count)

let unspacersView = viewsWithoutSpacerAndDivider()
Expand Down Expand Up @@ -215,7 +215,7 @@ extension VStackLayer {
extension VStackLayer {

// 取出固定 length 的 spacer
private func spacerSpecifyLength() -> CGFloat {
private func lengthOfAllFixedLengthSpacer() -> CGFloat {
spacerLayers()
.map({ $0.setLength })
.reduce(0, +)
Expand Down Expand Up @@ -268,7 +268,7 @@ extension VStackLayer {
}

let unspacerViewsMaxHeight = unspacerViewsHeight + unspacerViewsSpacing
let spacersHeight = (frame.height - unspacerViewsMaxHeight - self.spacerSpecifyLength())
let spacersHeight = (frame.height - unspacerViewsMaxHeight - self.lengthOfAllFixedLengthSpacer())
let spacerWidth = spacersHeight / CGFloat(self.dynamicSpacerLayers().count)

let spacerViews = self.spacerLayers()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ open class VStackLayerContainerView: UIView {
vStackLayer.sizeThatFits(size)
}

open override func sizeToFit() {
vStackLayer.sizeToFit()
}

open func addContent(@_StackKitVStackLayerContentResultBuilder _ content: () -> [CALayer]) {
vStackLayer.addContent(content)
}
Expand Down
Loading