Skip to content

Commit

Permalink
Add MMMFlowLayoutContainer
Browse files Browse the repository at this point in the history
  • Loading branch information
aleh committed May 24, 2024
1 parent 445b639 commit d5fd6f3
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 2 deletions.
2 changes: 1 addition & 1 deletion MMMCommonUI.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Pod::Spec.new do |s|

s.name = "MMMCommonUI"
s.version = "3.11.2"
s.version = "3.12.0"
s.summary = "Small UI-related pieces reused in many components from MMMTemple"
s.description = "#{s.summary}."
s.homepage = "https://github.com/mediamonks/#{s.name}"
Expand Down
118 changes: 118 additions & 0 deletions Sources/MMMCommonUI/MMMFlowLayoutContainer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//
// Starbucks App.
// Copyright (c) 2024 MediaMonks. All rights reserved.
//

import UIKit

/// A container arranging subviews as words on a page.
///
/// Note that calling ``systemLayoutSizeFitting()`` won't work properly here as our height depends on width.
///
/// Things that could be improved:
/// - recycle horizontal (row) stack views;
/// - use UILayoutGuide's directly instead of stack views.
public class MMMFlowLayoutContainer: NonStoryboardableView {

private let insets: UIEdgeInsets
private let horizontalSpacing: CGFloat
private let verticalAlignment: MMMLayoutVerticalAlignment
private let horizontalFittingPriority: UILayoutPriority
private let verticalStack: MMMVerticalStackContainer

public init(
horizontalSpacing: CGFloat,
verticalSpacing: CGFloat,
insets: UIEdgeInsets = .zero,
horizontalAlignment: MMMLayoutHorizontalAlignment = .left,
verticalAlignment: MMMLayoutVerticalAlignment = .center,
horizontalSizingPriority: UILayoutPriority = .defaultLow - 1
) {
self.insets = insets
self.verticalStack = .init(insets: insets, alignment: horizontalAlignment, spacing: verticalSpacing)
self.horizontalSpacing = horizontalSpacing
self.verticalAlignment = verticalAlignment
self.horizontalFittingPriority = horizontalSizingPriority

super.init()

addSubview(verticalStack)

mmm_addConstraintsAligningView(verticalStack, horizontally: .fill, vertically: .fill)
}

private var arrangedSubviews: [UIView] = []

public func setSubviews(_ subviews: [UIView]) {
guard self.arrangedSubviews != subviews else {
return
}
arrangedSubviews.forEach { $0.removeFromSuperview() }
self.arrangedSubviews = subviews
arrangedSubviews.forEach { addSubview($0) }
setNeedsUpdateConstraints()
}

private var lastConstrainedWidth: CGFloat = 0

public override func updateConstraints() {

super.updateConstraints()

lastConstrainedWidth = bounds.width

let totalWidth = bounds.inset(by: insets).width
guard totalWidth > 0 else {
verticalStack.setSubviews([])
return
}

var rows: [MMMHorizontalStackContainer] = []
var currentRow: [UIView] = []
var remainingWidth: CGFloat = 0

func flush() {
remainingWidth = totalWidth
guard !currentRow.isEmpty else {
return
}
let rowStack = MMMHorizontalStackContainer(insets: .zero, alignment: verticalAlignment, spacing: horizontalSpacing)
rowStack.setSubviews(currentRow)
rows.append(rowStack)
currentRow.removeAll()
}

func measure(_ subview: UIView) -> CGFloat {
ceil(subview.systemLayoutSizeFitting(
.init(remainingWidth, 0),
withHorizontalFittingPriority: horizontalFittingPriority, verticalFittingPriority: .fittingSizeLevel
).width)
}

flush()

for subview in arrangedSubviews {
var w = measure(subview)
if w > remainingWidth {
flush()
w = measure(subview)
}
currentRow.append(subview)
remainingWidth -= w + horizontalSpacing
if remainingWidth <= 0 {
flush()
}
}
flush()

verticalStack.setSubviews(rows)
}

public override func layoutSubviews() {
if lastConstrainedWidth != bounds.width {
setNeedsUpdateConstraints()
updateConstraintsIfNeeded()
}
super.layoutSubviews()
}
}
1 change: 0 additions & 1 deletion Sources/MMMCommonUIObjC/MMMCommonUI.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#import "MMMAnimations.h"
#import "MMMAutoLayoutScrollView.h"
#import "MMMCollectionView.h"
#import "MMMCommonUI.h"
#import "MMMCommonUIMisc.h"
#import "MMMContainerView.h"
#import "MMMImageView.h"
Expand Down

0 comments on commit d5fd6f3

Please sign in to comment.