Skip to content
Open
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
144 changes: 97 additions & 47 deletions Sources/AloeStackView/AloeStackView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,35 +64,45 @@ open class AloeStackView: UIScrollView {
/// Adds a row to the end of the stack view.
///
/// If `animated` is `true`, the insertion is animated.
open func addRow(_ row: UIView, animated: Bool = false) {
insertCell(withContentView: row, atIndex: stackView.arrangedSubviews.count, animated: animated)
open func addRow(_ row: UIView, animated: Bool = false, completion: ((Bool) -> Void)? = nil) {
insertCell(withContentView: row,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the parameter should start on the next line:

insertCell(
  withContentView: row,
  atIndex: stackView.arrangedSubviews.count,
  animated: animated,
  completion: completion)

if you could apply this style to the other callsites that would be great!

atIndex: stackView.arrangedSubviews.count,
animated: animated,
completion: completion)
}

/// Adds multiple rows to the end of the stack view.
///
/// If `animated` is `true`, the insertions are animated.
open func addRows(_ rows: [UIView], animated: Bool = false) {
rows.forEach { addRow($0, animated: animated) }
open func addRows(_ rows: [UIView], animated: Bool = false, completion: ((Bool) -> Void)? = nil) {
let group = DispatchGroup()
rows.forEach { group.enter(); addRow($0, animated: animated) { _ in group.leave() } }
group.notify(queue: .main) { completion?(true) }
}

/// Adds a row to the beginning of the stack view.
///
/// If `animated` is `true`, the insertion is animated.
open func prependRow(_ row: UIView, animated: Bool = false) {
insertCell(withContentView: row, atIndex: 0, animated: animated)
open func prependRow(_ row: UIView, animated: Bool = false, completion: ((Bool) -> Void)? = nil) {
insertCell(withContentView: row, atIndex: 0, animated: animated, completion: completion)
}

/// Adds multiple rows to the beginning of the stack view.
///
/// If `animated` is `true`, the insertions are animated.
open func prependRows(_ rows: [UIView], animated: Bool = false) {
rows.reversed().forEach { prependRow($0, animated: animated) }
open func prependRows(_ rows: [UIView], animated: Bool = false, completion: ((Bool) -> Void)? = nil) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please wrap these function signatures on to multiple lines

open func prependRows(
    _ rows: [UIView],
    animated: Bool = false,
    completion: ((Bool) -> Void)? = nil)
{

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i broke the line of function name that longer then 110 characters.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

however, here is my break line style.

open func prependRows(_ rows: [UIView],
                      animated: Bool = false,
                      completion: ((Bool) -> Void)? = nil) {
       // code is here
}

if you don't like this style please commit the additional feedback. i will apply as soon as possible.

let group = DispatchGroup()
rows.reversed().forEach { group.enter(); prependRow($0, animated: animated) { _ in group.leave() } }
group.notify(queue: .main) { completion?(true) }
}

/// Inserts a row above the specified row in the stack view.
///
/// If `animated` is `true`, the insertion is animated.
open func insertRow(_ row: UIView, before beforeRow: UIView, animated: Bool = false) {
open func insertRow(_ row: UIView,
before beforeRow: UIView,
animated: Bool = false,
completion: ((Bool) -> Void)? = nil) {
#if swift(>=5.0)
guard
let cell = beforeRow.superview as? StackViewCell,
Expand All @@ -102,20 +112,30 @@ open class AloeStackView: UIScrollView {
let cell = beforeRow.superview as? StackViewCell,
let index = stackView.arrangedSubviews.index(of: cell) else { return }
#endif
insertCell(withContentView: row, atIndex: index, animated: animated)
insertCell(withContentView: row, atIndex: index, animated: animated, completion: completion)
}

/// Inserts multiple rows above the specified row in the stack view.
///
/// If `animated` is `true`, the insertions are animated.
open func insertRows(_ rows: [UIView], before beforeRow: UIView, animated: Bool = false) {
rows.forEach { insertRow($0, before: beforeRow, animated: animated) }
open func insertRows(_ rows: [UIView],
before beforeRow: UIView,
animated: Bool = false,
completion: ((Bool) -> Void)? = nil) {
let group = DispatchGroup()
rows.forEach {
group.enter()
insertRow($0, before: beforeRow, animated: animated) { _ in group.leave() } }
group.notify(queue: .main) { completion?(true) }
}

/// Inserts a row below the specified row in the stack view.
///
/// If `animated` is `true`, the insertion is animated.
open func insertRow(_ row: UIView, after afterRow: UIView, animated: Bool = false) {
open func insertRow(_ row: UIView,
after afterRow: UIView,
animated: Bool = false,
completion: ((Bool) -> Void)? = nil) {
#if swift(>=5.0)
guard
let cell = afterRow.superview as? StackViewCell,
Expand All @@ -125,44 +145,55 @@ open class AloeStackView: UIScrollView {
let cell = afterRow.superview as? StackViewCell,
let index = stackView.arrangedSubviews.index(of: cell) else { return }
#endif
insertCell(withContentView: row, atIndex: index + 1, animated: animated)
insertCell(withContentView: row, atIndex: index + 1, animated: animated, completion: completion)
}

/// Inserts multiple rows below the specified row in the stack view.
///
/// If `animated` is `true`, the insertions are animated.
open func insertRows(_ rows: [UIView], after afterRow: UIView, animated: Bool = false) {
open func insertRows(_ rows: [UIView],
after afterRow: UIView,
animated: Bool = false,
completion: ((Bool) -> Void)? = nil) {
let group = DispatchGroup()
_ = rows.reduce(afterRow) { currentAfterRow, row in
insertRow(row, after: currentAfterRow, animated: animated)
group.enter()
insertRow(row, after: currentAfterRow, animated: animated) { _ in group.leave() }
return row
}
group.notify(queue: .main) { completion?(true) }
}

/// Removes the given row from the stack view.
///
/// If `animated` is `true`, the removal is animated.
open func removeRow(_ row: UIView, animated: Bool = false) {
open func removeRow(_ row: UIView, animated: Bool = false, completion: ((Bool) -> Void)? = nil) {
if let cell = row.superview as? StackViewCell {
removeCell(cell, animated: animated)
removeCell(cell, animated: animated, completion: completion)
}
}

/// Removes the given rows from the stack view.
///
/// If `animated` is `true`, the removals are animated.
open func removeRows(_ rows: [UIView], animated: Bool = false) {
rows.forEach { removeRow($0, animated: animated) }
open func removeRows(_ rows: [UIView], animated: Bool = false, completion: ((Bool) -> Void)? = nil) {
let group = DispatchGroup()
rows.forEach { group.enter(); removeRow($0, animated: animated) { _ in group.leave() } }
group.notify(queue: .main) { completion?(true) }
}

/// Removes all the rows in the stack view.
///
/// If `animated` is `true`, the removals are animated.
open func removeAllRows(animated: Bool = false) {
open func removeAllRows(animated: Bool = false, completion: ((Bool) -> Void)? = nil) {
let group = DispatchGroup()
stackView.arrangedSubviews.forEach { view in
if let cell = view as? StackViewCell {
removeRow(cell.contentView, animated: animated)
group.enter()
removeRow(cell.contentView, animated: animated) { _ in group.leave() }
}
}
group.notify(queue: .main) { completion?(true) }
}

// MARK: Accessing Rows
Expand Down Expand Up @@ -205,53 +236,68 @@ open class AloeStackView: UIScrollView {
/// Hides the given row, making it invisible.
///
/// If `animated` is `true`, the change is animated.
open func hideRow(_ row: UIView, animated: Bool = false) {
setRowHidden(row, isHidden: true, animated: animated)
open func hideRow(_ row: UIView, animated: Bool = false, completion: ((Bool) -> Void)? = nil) {
setRowHidden(row, isHidden: true, animated: animated, completion: completion)
}

/// Hides the given rows, making them invisible.
///
/// If `animated` is `true`, the changes are animated.
open func hideRows(_ rows: [UIView], animated: Bool = false) {
rows.forEach { hideRow($0, animated: animated) }
open func hideRows(_ rows: [UIView], animated: Bool = false, completion: ((Bool) -> Void)? = nil) {
let group = DispatchGroup()
rows.forEach { group.enter(); hideRow($0, animated: animated) { _ in group.leave() } }
group.notify(queue: .main) { completion?(true) }
}

/// Shows the given row, making it visible.
///
/// If `animated` is `true`, the change is animated.
open func showRow(_ row: UIView, animated: Bool = false) {
setRowHidden(row, isHidden: false, animated: animated)
open func showRow(_ row: UIView, animated: Bool = false, completion: ((Bool) -> Void)? = nil) {
setRowHidden(row, isHidden: false, animated: animated, completion: completion)
}

/// Shows the given rows, making them visible.
///
/// If `animated` is `true`, the changes are animated.
open func showRows(_ rows: [UIView], animated: Bool = false) {
rows.forEach { showRow($0, animated: animated) }
open func showRows(_ rows: [UIView], animated: Bool = false, completion: ((Bool) -> Void)? = nil) {
let group = DispatchGroup()
rows.forEach { group.enter(); showRow($0, animated: animated) { _ in group.leave() } }
group.notify(queue: .main) { completion?(true) }
}

/// Hides the given row if `isHidden` is `true`, or shows the given row if `isHidden` is `false`.
///
/// If `animated` is `true`, the change is animated.
open func setRowHidden(_ row: UIView, isHidden: Bool, animated: Bool = false) {
open func setRowHidden(_ row: UIView,
isHidden: Bool,
animated: Bool = false,
completion: ((Bool) -> Void)? = nil) {
guard let cell = row.superview as? StackViewCell, cell.isHidden != isHidden else { return }

if animated {
UIView.animate(withDuration: 0.3) {
UIView.animate(withDuration: 0.3, animations: {
cell.isHidden = isHidden
cell.layoutIfNeeded()
}
}, completion: completion)
} else {
cell.isHidden = isHidden
completion?(true)
}
}

/// Hides the given rows if `isHidden` is `true`, or shows the given rows if `isHidden` is
/// `false`.
///
/// If `animated` is `true`, the change are animated.
open func setRowsHidden(_ rows: [UIView], isHidden: Bool, animated: Bool = false) {
rows.forEach { setRowHidden($0, isHidden: isHidden, animated: animated) }
open func setRowsHidden(_ rows: [UIView],
isHidden: Bool,
animated: Bool = false,
completion: ((Bool) -> Void)? = nil) {
let group = DispatchGroup()
rows.forEach {
group.enter()
setRowHidden($0, isHidden: isHidden, animated: animated) { _ in group.leave() } }
group.notify(queue: .main) { completion?(true) }
}

/// Returns `true` if the given row is hidden, `false` otherwise.
Expand Down Expand Up @@ -529,7 +575,10 @@ open class AloeStackView: UIScrollView {
return cell
}

private func insertCell(withContentView contentView: UIView, atIndex index: Int, animated: Bool) {
private func insertCell(withContentView contentView: UIView,
atIndex index: Int,
animated: Bool,
completion: ((Bool) -> Void)? = nil) {
let cellToRemove = containsRow(contentView) ? contentView.superview : nil

let cell = createCell(withContentView: contentView)
Expand All @@ -551,16 +600,18 @@ open class AloeStackView: UIScrollView {
if animated {
cell.alpha = 0
layoutIfNeeded()
UIView.animate(withDuration: 0.3) {
UIView.animate(withDuration: 0.3, animations: {
cell.alpha = 1
}
}, completion: completion)
} else {
completion?(true)
}
}

private func removeCell(_ cell: StackViewCell, animated: Bool) {
private func removeCell(_ cell: StackViewCell, animated: Bool, completion: ((Bool) -> Void)? = nil) {
let aboveCell = cellAbove(cell: cell)

let completion: (Bool) -> Void = { [weak self] _ in
let completionHandler: (Bool) -> Void = { [weak self] finished in
guard let `self` = self else { return }
cell.removeFromSuperview()

Expand All @@ -569,17 +620,16 @@ open class AloeStackView: UIScrollView {
if let aboveCell = aboveCell {
self.updateSeparatorVisibility(forCell: aboveCell)
}

completion?(finished)
}

if animated {
UIView.animate(
withDuration: 0.3,
animations: {
cell.isHidden = true
},
completion: completion)
UIView.animate(withDuration: 0.3, animations: {
cell.isHidden = true
}, completion: completionHandler)
} else {
completion(true)
completionHandler(true)
}
}

Expand Down