Skip to content

Commit

Permalink
New ScrollStackView
Browse files Browse the repository at this point in the history
  • Loading branch information
kennic committed Jun 23, 2020
1 parent 470ff6d commit 80410e5
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 24 deletions.
Binary file not shown.
2 changes: 1 addition & 1 deletion Example/FrameLayoutKit/CardView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class CardView: UIView {

frameLayout.spacing = 15.0
frameLayout.padding(top: 15, left: 15, bottom: 15, right: 15)
frameLayout.debug = true
// frameLayout.debug = true
addSubview(frameLayout)
}

Expand Down
28 changes: 9 additions & 19 deletions Example/FrameLayoutKit/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,28 @@ import UIKit
import FrameLayoutKit

class ViewController: UIViewController {
let frameLayout = StackFrameLayout(axis: .vertical)
let scrollStackView = ScrollStackView()

override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = .lightGray

#if targetEnvironment(macCatalyst)
for _ in 0..<1 {
let cardView = CardView()
view.addSubview(cardView)
frameLayout.add(cardView)
for _ in 0..<5 {
scrollStackView.add(CardView())
}
#else
let cardView = CardView()
view.addSubview(cardView)
frameLayout.add(cardView)
#endif

let numberPadView = NumberPadView()
view.addSubview(numberPadView)
frameLayout.add(numberPadView)
scrollStackView.add(NumberPadView())

frameLayout.spacing = 20
frameLayout.edgeInsets = UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50)
frameLayout.distribution = .center
view.addSubview(frameLayout)
scrollStackView.spacing = 20
scrollStackView.edgeInsets = UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50)
scrollStackView.distribution = .center
view.addSubview(scrollStackView)
}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
frameLayout.frame = view.bounds
scrollStackView.frame = view.bounds
}

}
6 changes: 3 additions & 3 deletions Example/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PODS:
- FrameLayoutKit (4.4.1)
- FrameLayoutKit (4.5.0)

DEPENDENCIES:
- FrameLayoutKit (from `../`)
Expand All @@ -9,8 +9,8 @@ EXTERNAL SOURCES:
:path: "../"

SPEC CHECKSUMS:
FrameLayoutKit: 59ea4b37f24e6b364048b49f1d5e57e976dca444
FrameLayoutKit: d85f746072cc137c13647fbb2be252bdc0fb1b60

PODFILE CHECKSUM: ac097e09a36888b120812ddf1779def87475dc02

COCOAPODS: 1.9.1
COCOAPODS: 1.9.3
2 changes: 1 addition & 1 deletion FrameLayoutKit.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'FrameLayoutKit'
s.version = '4.4.2'
s.version = '4.5.0'
s.summary = 'FrameLayoutKit is a super fast and easy to use layout kit'
s.description = <<-DESC
An auto layout kit helps you to layout your UI easier and more effective
Expand Down
228 changes: 228 additions & 0 deletions FrameLayoutKit/Classes/ScrollStackView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
//
// ScrollStackView.swift
// FrameLayoutKit
//
// Created by Nam Kennic on 6/23/20.
//

import UIKit

open class ScrollStackView<T: UIView>: UIView {

public var views: [T] = [] {
didSet {
updateLayout()
setNeedsLayout()
}
}

public var firstView: T? {
return frameLayout.firstFrameLayout?.targetView as? T
}

public var lastView: T? {
return frameLayout.lastFrameLayout?.targetView as? T
}

open var spacing: CGFloat {
get {
return frameLayout.spacing
}
set {
frameLayout.spacing = newValue
setNeedsLayout()
}
}

open var edgeInsets: UIEdgeInsets {
get {
return frameLayout.edgeInsets
}
set {
frameLayout.edgeInsets = newValue
setNeedsLayout()
}
}

override open var frame: CGRect {
didSet {
setNeedsLayout()
}
}

override open var bounds: CGRect {
didSet {
setNeedsLayout()
}
}

public var axis: NKLayoutAxis {
get {
return frameLayout.axis
}
set {
frameLayout.axis = newValue
setNeedsLayout()
}
}

public var distribution: NKLayoutDistribution {
get {
return frameLayout.distribution
}
set {
frameLayout.distribution = newValue
setNeedsLayout()
}
}

public let scrollView = UIScrollView()
public let frameLayout = StackFrameLayout(axis: .vertical, distribution: .top)

// MARK: -

convenience public init(views: [T], axis: NKLayoutAxis = .vertical) {
self.init()

self.axis = axis
defer {
self.views = views
}
}

public init() {
super.init(frame: .zero)

scrollView.bounces = true
scrollView.alwaysBounceHorizontal = false
scrollView.alwaysBounceVertical = false
scrollView.isDirectionalLockEnabled = true
scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false
scrollView.clipsToBounds = false
scrollView.delaysContentTouches = false

frameLayout.spacing = 0.0
frameLayout.isIntrinsicSizeEnabled = true
frameLayout.shouldCacheSize = false
scrollView.addSubview(frameLayout)
addSubview(scrollView)
}

required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

override open func sizeThatFits(_ size: CGSize) -> CGSize {
return frameLayout.sizeThatFits(size)
}

override open func layoutSubviews() {
super.layoutSubviews()

let viewSize = bounds.size
let sizeToFit = axis == .horizontal ? CGSize(width: CGFloat.infinity, height: viewSize.height) : CGSize(width: viewSize.width, height: CGFloat.infinity)
scrollView.contentSize = frameLayout.sizeThatFits(sizeToFit)
scrollView.frame = bounds

var contentFrame = bounds
if axis == .horizontal {
contentFrame.size.width = max(viewSize.width, scrollView.contentSize.width)
scrollView.contentSize.height = min(viewSize.height, scrollView.contentSize.height)
}
else {
contentFrame.size.height = max(viewSize.height, scrollView.contentSize.height)
scrollView.contentSize.width = min(viewSize.width, scrollView.contentSize.width)
}
frameLayout.frame = contentFrame
}

// MARK: -

public func view(at index: Int) -> T? {
return frameLayout.frameLayout(at: index)?.targetView as? T
}

@discardableResult
open func add(_ view: T?) -> FrameLayout {
if let view = view { scrollView.addSubview(view) }
let layout = frameLayout.add(view)
setNeedsLayout()
return layout
}

@discardableResult
open func add(_ views: [T]) -> [FrameLayout] {
var results = [FrameLayout]()
views.forEach { (view) in
results.append(add(view))
}

return results
}

@discardableResult
open func insert(_ view: T?, at index: Int) -> FrameLayout {
if let view = view { scrollView.addSubview(view) }
let layout = frameLayout.insert(view, at: index)
setNeedsLayout()
return layout
}

@discardableResult
open func addSpace(_ size: CGFloat = 0) -> FrameLayout {
let layout = add(T())
layout.fixSize = CGSize(width: axis == .horizontal ? size : 0, height: axis == .vertical ? size : 0)
return layout
}

open func replace(view: T, at index: Int) {
self.view(at: index)?.removeFromSuperview()
scrollView.addSubview(view)
frameLayout.frameLayout(at: index)?.targetView = view
setNeedsLayout()
}

open func removeView(at index: Int) {
frameLayout.removeFrameLayout(at: index, autoRemoveTargetView: true)
setNeedsLayout()
}

open func removeAll() {
views = []
}

// MARK: -

fileprivate func updateLayout() {
if views.isEmpty {
frameLayout.enumerate({ (layout, index, stop) in
layout.targetView?.removeFromSuperview()
})

frameLayout.removeAll(autoRemoveTargetView: true)
}
else {
let total = views.count

if frameLayout.frameLayouts.count > total {
frameLayout.enumerate({ (layout, index, stop) in
if Int(index) >= Int(total) {
layout.targetView?.removeFromSuperview()
}
})
}

frameLayout.numberOfFrameLayouts = total

frameLayout.enumerate({ (layout, idx, stop) in
let view = views[idx]
scrollView.addSubview(view)
layout.targetView = view
})
}

setNeedsLayout()
}

}

0 comments on commit 80410e5

Please sign in to comment.