diff --git a/AloeStackView.xcodeproj/project.pbxproj b/AloeStackView.xcodeproj/project.pbxproj index 737ef3f..f7f2345 100644 --- a/AloeStackView.xcodeproj/project.pbxproj +++ b/AloeStackView.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 4A67EB2121868D2C00D134F7 /* CustomAnimating.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A67EB2021868D2C00D134F7 /* CustomAnimating.swift */; }; + 4A67EB262186A94F00D134F7 /* State.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A67EB252186A94F00D134F7 /* State.swift */; }; + 4A67EB282186AB0400D134F7 /* AnimationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A67EB272186AB0400D134F7 /* AnimationCoordinator.swift */; }; A74F6EDB216D5CB50054AA18 /* AloeStackView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A74F6ED1216D5CB50054AA18 /* AloeStackView.framework */; }; A74F6EF6216D5EFF0054AA18 /* AloeStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74F6EED216D5EFE0054AA18 /* AloeStackView.swift */; }; A74F6EF7216D5EFF0054AA18 /* AloeStackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74F6EEE216D5EFE0054AA18 /* AloeStackViewController.swift */; }; @@ -29,6 +32,9 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 4A67EB2021868D2C00D134F7 /* CustomAnimating.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAnimating.swift; sourceTree = ""; }; + 4A67EB252186A94F00D134F7 /* State.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = State.swift; sourceTree = ""; }; + 4A67EB272186AB0400D134F7 /* AnimationCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimationCoordinator.swift; sourceTree = ""; }; A74F6ED1216D5CB50054AA18 /* AloeStackView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AloeStackView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A74F6EDA216D5CB50054AA18 /* AloeStackViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AloeStackViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A74F6EED216D5EFE0054AA18 /* AloeStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AloeStackView.swift; sourceTree = ""; }; @@ -64,6 +70,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 4A67EB222186A92100D134F7 /* Animation */ = { + isa = PBXGroup; + children = ( + 4A67EB272186AB0400D134F7 /* AnimationCoordinator.swift */, + 4A67EB252186A94F00D134F7 /* State.swift */, + ); + path = Animation; + sourceTree = ""; + }; A74F6EC7216D5CB40054AA18 = { isa = PBXGroup; children = ( @@ -101,6 +116,7 @@ A74F6EEE216D5EFE0054AA18 /* AloeStackViewController.swift */, A74F6EEF216D5EFE0054AA18 /* Views */, A74F6EF2216D5EFE0054AA18 /* Protocols */, + 4A67EB222186A92100D134F7 /* Animation */, ); path = AloeStackView; sourceTree = ""; @@ -120,6 +136,7 @@ A74F6EF3216D5EFE0054AA18 /* Tappable.swift */, A74F6EF4216D5EFE0054AA18 /* SeparatorHiding.swift */, A74F6EF5216D5EFE0054AA18 /* Highlightable.swift */, + 4A67EB2021868D2C00D134F7 /* CustomAnimating.swift */, ); path = Protocols; sourceTree = ""; @@ -249,7 +266,10 @@ buildActionMask = 2147483647; files = ( A74F6EF8216D5EFF0054AA18 /* SeparatorView.swift in Sources */, + 4A67EB2121868D2C00D134F7 /* CustomAnimating.swift in Sources */, + 4A67EB282186AB0400D134F7 /* AnimationCoordinator.swift in Sources */, A74F6EF7216D5EFF0054AA18 /* AloeStackViewController.swift in Sources */, + 4A67EB262186A94F00D134F7 /* State.swift in Sources */, A74F6EF6216D5EFF0054AA18 /* AloeStackView.swift in Sources */, A74F6EF9216D5EFF0054AA18 /* StackViewCell.swift in Sources */, A74F6EFC216D5EFF0054AA18 /* Highlightable.swift in Sources */, diff --git a/Example/AloeStackViewExample.xcodeproj/project.pbxproj b/Example/AloeStackViewExample.xcodeproj/project.pbxproj index ce2d73d..c4687ff 100644 --- a/Example/AloeStackViewExample.xcodeproj/project.pbxproj +++ b/Example/AloeStackViewExample.xcodeproj/project.pbxproj @@ -9,6 +9,9 @@ /* Begin PBXBuildFile section */ 4D42E5BA22E499FE00D51F93 /* AloeStackView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7497A9021746D9700BB692A /* AloeStackView.framework */; }; 4D42E5BB22E499FE00D51F93 /* AloeStackView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A7497A9021746D9700BB692A /* AloeStackView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 4A67EB2E2186D8C100D134F7 /* TitleCaptionRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A67EB2D2186D8C100D134F7 /* TitleCaptionRowView.swift */; }; + 4A67EB302186D91F00D134F7 /* CustomAnimatingLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A67EB2F2186D91F00D134F7 /* CustomAnimatingLabel.swift */; }; + A7497A9121746D9700BB692A /* AloeStackView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7497A9021746D9700BB692A /* AloeStackView.framework */; }; A7497A932174725700BB692A /* SwitchRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7497A922174725700BB692A /* SwitchRowView.swift */; }; A7497A9721747DD600BB692A /* PhotoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7497A9621747DD600BB692A /* PhotoViewController.swift */; }; A7497A9921755AD100BB692A /* ExpandingRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7497A9821755AD100BB692A /* ExpandingRowView.swift */; }; @@ -33,6 +36,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 4A67EB2D2186D8C100D134F7 /* TitleCaptionRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleCaptionRowView.swift; sourceTree = ""; }; + 4A67EB2F2186D91F00D134F7 /* CustomAnimatingLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAnimatingLabel.swift; sourceTree = ""; }; A7420FAB21715DF500F2A343 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; A7497A9021746D9700BB692A /* AloeStackView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AloeStackView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A7497A922174725700BB692A /* SwitchRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchRowView.swift; sourceTree = ""; }; @@ -94,8 +99,10 @@ A7420FA921715CC400F2A343 /* Views */ = { isa = PBXGroup; children = ( + 4A67EB2D2186D8C100D134F7 /* TitleCaptionRowView.swift */, A7497A922174725700BB692A /* SwitchRowView.swift */, A7497A9821755AD100BB692A /* ExpandingRowView.swift */, + 4A67EB2F2186D91F00D134F7 /* CustomAnimatingLabel.swift */, ); path = Views; sourceTree = ""; @@ -216,6 +223,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4A67EB302186D91F00D134F7 /* CustomAnimatingLabel.swift in Sources */, + 4A67EB2E2186D8C100D134F7 /* TitleCaptionRowView.swift in Sources */, A74F70BB217155FC0054AA18 /* MainViewController.swift in Sources */, A7497A9721747DD600BB692A /* PhotoViewController.swift in Sources */, A74F70B9217155FC0054AA18 /* AppDelegate.swift in Sources */, diff --git a/Example/AloeStackViewExample/ViewControllers/MainViewController.swift b/Example/AloeStackViewExample/ViewControllers/MainViewController.swift index 58b14bd..6569e72 100644 --- a/Example/AloeStackViewExample/ViewControllers/MainViewController.swift +++ b/Example/AloeStackViewExample/ViewControllers/MainViewController.swift @@ -41,17 +41,16 @@ public class MainViewController: AloeStackViewController { setUpDescriptionRow() setUpSwitchRow() setUpHiddenRows() + setUpCustomAnimatingDescriptionRow() + setUpCustomAnimatingRow() setUpExpandingRowView() setUpHorizontalRow() setUpPhotoRow() } private func setUpDescriptionRow() { - let label = UILabel() - label.font = UIFont.preferredFont(forTextStyle: .body) - label.numberOfLines = 0 - label.text = "This simple app shows some ways you can use AloeStackView to lay out a screen in your app." - stackView.addRow(label) + let descriptionRow = TitleCaptionRowView(titleText: "This simple app shows some ways you can use AloeStackView to lay out a screen in your app.") + stackView.addRow(descriptionRow) } private func setUpSwitchRow() { @@ -91,6 +90,33 @@ public class MainViewController: AloeStackViewController { stackView.setSeparatorInset(forRows: Array(hiddenRows.dropLast()), inset: separatorInset) } + private func setUpCustomAnimatingDescriptionRow() { + let animatableRow = TitleCaptionRowView(titleText: "Customizing Row Animation", captionText: "(Try tapping on the Row!)") + stackView.addRow(animatableRow) + stackView.setTapHandler(forRow: animatableRow) { [weak self] _ in + guard let `self` = self else { return } + let isHidden = self.stackView.isRowHidden(self.customAnimatingLabel) + self.stackView.setRowHidden(self.customAnimatingLabel, isHidden: !isHidden, animated: true) + } + } + + private let customAnimatingLabel = CustomAnimatingLabel() + + private func setUpCustomAnimatingRow() { + customAnimatingLabel.text = "Customizing Row Animation" + + stackView.addRow(customAnimatingLabel) + stackView.hideRow(customAnimatingLabel) + + let rowInset = UIEdgeInsets( + top: stackView.rowInset.top, + left: stackView.rowInset.left * 2, + bottom: stackView.rowInset.bottom, + right: stackView.rowInset.right) + + stackView.setInset(forRow: customAnimatingLabel, inset: rowInset) + } + private func setUpExpandingRowView() { let expandingRow = ExpandingRowView() stackView.addRow(expandingRow) @@ -168,30 +194,9 @@ public class MainViewController: AloeStackViewController { } private func setUpPhotoRow() { - let titleLabel = UILabel() - titleLabel.font = UIFont.preferredFont(forTextStyle: .body) - titleLabel.numberOfLines = 0 - titleLabel.text = "Handle user interaction" - stackView.addRow(titleLabel) - stackView.hideSeparator(forRow: titleLabel) - stackView.setInset(forRow: titleLabel, inset: UIEdgeInsets( - top: stackView.rowInset.top, - left: stackView.rowInset.left, - bottom: 4, - right: stackView.rowInset.right)) - - let captionLabel = UILabel() - captionLabel.font = UIFont.preferredFont(forTextStyle: .caption2) - captionLabel.textColor = .blue - captionLabel.numberOfLines = 0 - captionLabel.text = "(Try tapping on the photo!)" - stackView.addRow(captionLabel) - stackView.hideSeparator(forRow: captionLabel) - stackView.setInset(forRow: captionLabel, inset: UIEdgeInsets( - top: 0, - left: stackView.rowInset.left, - bottom: stackView.rowInset.bottom, - right: stackView.rowInset.right)) + let row = TitleCaptionRowView(titleText: "Handle user interaction", captionText: "(Try tapping on the photo!)") + stackView.addRow(row) + stackView.hideSeparator(forRow: row) guard let image = UIImage(named: "lobster-dog") else { return } let aspectRatio = image.size.height / image.size.width diff --git a/Example/AloeStackViewExample/Views/CustomAnimatingLabel.swift b/Example/AloeStackViewExample/Views/CustomAnimatingLabel.swift new file mode 100644 index 0000000..c898e52 --- /dev/null +++ b/Example/AloeStackViewExample/Views/CustomAnimatingLabel.swift @@ -0,0 +1,36 @@ +// Created by gwangbeom on 10/29/18. +// Copyright 2018 Airbnb, Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import UIKit +import AloeStackView + +public class CustomAnimatingLabel: UILabel, CustomAnimating { + + public func animateInsert() { + transform = .identity + alpha = 1 + } + + public func insertAnimationWillBegin() { + transform = CGAffineTransform(translationX: -100, y: 0) + alpha = 0 + } + + public func animateRemove() { + transform = CGAffineTransform(translationX: -100, y: 0) + alpha = 0 + } + +} diff --git a/Example/AloeStackViewExample/Views/ExpandingRowView.swift b/Example/AloeStackViewExample/Views/ExpandingRowView.swift index 32c20b8..76c11bd 100644 --- a/Example/AloeStackViewExample/Views/ExpandingRowView.swift +++ b/Example/AloeStackViewExample/Views/ExpandingRowView.swift @@ -16,13 +16,12 @@ import AloeStackView import UIKit -public class ExpandingRowView: UIStackView, Tappable, Highlightable { +public class ExpandingRowView: TitleCaptionRowView, Tappable, Highlightable { // MARK: Lifecycle public init() { - super.init(frame: .zero) - translatesAutoresizingMaskIntoConstraints = false + super.init(titleText: "Dynamically change row content", captionText: "(Tap on this row to add more content!)\n") setUpViews() } @@ -42,38 +41,14 @@ public class ExpandingRowView: UIStackView, Tappable, Highlightable { // MARK: Private - private let titleLabel = UILabel() - private let showMoreLabel = UILabel() private let textLabel = UILabel() private var nextLine = 1 private func setUpViews() { - setUpSelf() - setUpTitleLabel() - setUpShowMoreLabel() setUpTextLabel() } - private func setUpSelf() { - axis = .vertical - spacing = 4 - } - - private func setUpTitleLabel() { - titleLabel.text = "Dynamically change row content" - titleLabel.font = UIFont.preferredFont(forTextStyle: .body) - addArrangedSubview(titleLabel) - } - - private func setUpShowMoreLabel() { - showMoreLabel.numberOfLines = 0 - showMoreLabel.text = "(Tap on this row to add more content!)\n" - showMoreLabel.font = UIFont.preferredFont(forTextStyle: .caption2) - showMoreLabel.textColor = .blue - addArrangedSubview(showMoreLabel) - } - private func setUpTextLabel() { textLabel.numberOfLines = 0 textLabel.font = UIFont.preferredFont(forTextStyle: .caption2) diff --git a/Example/AloeStackViewExample/Views/TitleCaptionRowView.swift b/Example/AloeStackViewExample/Views/TitleCaptionRowView.swift new file mode 100644 index 0000000..033f890 --- /dev/null +++ b/Example/AloeStackViewExample/Views/TitleCaptionRowView.swift @@ -0,0 +1,66 @@ +// Created by gwangbeom on 10/29/18. +// Copyright 2018 Airbnb, Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import UIKit + +public class TitleCaptionRowView: UIStackView { + + // MARK: Lifecycle + + public init(titleText: String, captionText: String? = nil) { + super.init(frame: .zero) + titleLabel.text = titleText + captionLabel.text = captionText + setUpViews() + } + + public required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Private + + private let titleLabel = UILabel() + private let captionLabel = UILabel() + + private func setUpSelf() { + translatesAutoresizingMaskIntoConstraints = false + axis = .vertical + spacing = 4 + } + + private func setUpViews() { + setUpSelf() + setUpTitleLabel() + setUpCaptionLabel() + } + + private func setUpTitleLabel() { + titleLabel.numberOfLines = 0 + titleLabel.translatesAutoresizingMaskIntoConstraints = false + titleLabel.font = UIFont.preferredFont(forTextStyle: .body) + addArrangedSubview(titleLabel) + } + + private func setUpCaptionLabel() { + guard captionLabel.text != nil else { return } + captionLabel.numberOfLines = 0 + captionLabel.translatesAutoresizingMaskIntoConstraints = false + captionLabel.font = UIFont.preferredFont(forTextStyle: .caption2) + captionLabel.textColor = .blue + addArrangedSubview(captionLabel) + } + +} diff --git a/Sources/AloeStackView/AloeStackView.swift b/Sources/AloeStackView/AloeStackView.swift index 07aa92f..2ed9280 100644 --- a/Sources/AloeStackView/AloeStackView.swift +++ b/Sources/AloeStackView/AloeStackView.swift @@ -237,10 +237,12 @@ open class AloeStackView: UIScrollView { guard let cell = row.superview as? StackViewCell, cell.isHidden != isHidden else { return } if animated { - UIView.animate(withDuration: 0.3) { + let state: State = isHidden ? .remove : .insert + let coordinator = AnimationCoordinator(target: cell, state: state, animations: { cell.isHidden = isHidden cell.layoutIfNeeded() - } + }) + coordinator.startAnimation() } else { cell.isHidden = isHidden } @@ -551,9 +553,10 @@ open class AloeStackView: UIScrollView { if animated { cell.alpha = 0 layoutIfNeeded() - UIView.animate(withDuration: 0.3) { + let coordinator = AnimationCoordinator(target: cell, state: .insert, animations: { cell.alpha = 1 - } + }) + coordinator.startAnimation() } } @@ -572,12 +575,10 @@ open class AloeStackView: UIScrollView { } if animated { - UIView.animate( - withDuration: 0.3, - animations: { - cell.isHidden = true - }, - completion: completion) + let coordinator = AnimationCoordinator(target: cell, state: .remove, animations: { + cell.isHidden = true + }, completion: completion) + coordinator.startAnimation() } else { completion(true) } diff --git a/Sources/AloeStackView/Animation/AnimationCoordinator.swift b/Sources/AloeStackView/Animation/AnimationCoordinator.swift new file mode 100644 index 0000000..f74fc1c --- /dev/null +++ b/Sources/AloeStackView/Animation/AnimationCoordinator.swift @@ -0,0 +1,82 @@ +// Created by gwangbeom on 10/29/18. +// Copyright 2018 Airbnb, Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import UIKit + +/** + * An object that animates when the Row is inserted and removed from the AloeStackView. + */ +internal class AnimationCoordinator { + + // MARK: Internal + + internal init(target: StackViewCell, state: State, animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) { + self.target = target + self.state = state + self.animations = animations + self.completion = completion + } + + internal func startAnimation() { + willBeginAnimation?() + UIView.animate(withDuration: AnimationCoordinator.defaultAnimationDuration, + delay: 0, + usingSpringWithDamping: animatable?.springDamping ?? 1, + initialSpringVelocity: 0, + options: [.curveEaseInOut, .allowUserInteraction, .beginFromCurrentState], + animations: { + self.animate?() + self.animations() + }) { finished in + self.animationDidEnd?(finished) + self.completion?(finished) + } + } + + // MARK: Private + + private let target: StackViewCell + private let state: State + private let animations: () -> Void + private let completion: ((Bool) -> Void)? + + private var animatable: CustomAnimating? { + return target.contentView as? CustomAnimating + } + + private lazy var willBeginAnimation: (() -> Void)? = { + switch state { + case .insert: return animatable?.insertAnimationWillBegin + case .remove: return animatable?.removeAnimationWillBegin + } + }() + + private lazy var animate: (() -> Void)? = { + switch state { + case .insert: return animatable?.animateInsert + case .remove: return animatable?.animateRemove + } + }() + + private lazy var animationDidEnd: ((Bool) -> Void)? = { + switch state { + case .insert: return animatable?.insertAnimationDidEnd + case .remove: return animatable?.removeAnimationDidEnd + } + }() + + private static let defaultAnimationDuration: TimeInterval = 0.3 + +} diff --git a/Sources/AloeStackView/Animation/State.swift b/Sources/AloeStackView/Animation/State.swift new file mode 100644 index 0000000..13c412c --- /dev/null +++ b/Sources/AloeStackView/Animation/State.swift @@ -0,0 +1,25 @@ +// Created by gwangbeom on 10/29/18. +// Copyright 2018 Airbnb, Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * Indicates that the row is inserted and removed from the AloeStackView. + */ +internal enum State { + + case insert + + case remove + +} diff --git a/Sources/AloeStackView/Protocols/CustomAnimating.swift b/Sources/AloeStackView/Protocols/CustomAnimating.swift new file mode 100644 index 0000000..93fc103 --- /dev/null +++ b/Sources/AloeStackView/Protocols/CustomAnimating.swift @@ -0,0 +1,70 @@ +// Created by gwangbeom on 10/29/18. +// Copyright 2018 Airbnb, Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import UIKit + +/** + * The rows in `AloeStackView` help Custom Animation to work. + * + * Rows that are added to an `AloeStackView` can conform to this protocol to have their + * You can freely change the state animation of each contentView when a row is inserted or removed. + */ +public protocol CustomAnimating { + + /// The springDamping value used to determine the amount of 'bounce' + /// seen when animationing to insert or remove state + /// + /// Default Value is 0.8. + var springDamping: CGFloat { get } + + /// Called before insert animation is activated. + func insertAnimationWillBegin() + + /// Called when insert animation is working. + func animateInsert() + + /// Called after insert animation. + func insertAnimationDidEnd(_ finished: Bool) + + /// Called before remove animation is activated. + func removeAnimationWillBegin() + + /// Called when remove animation is working. + func animateRemove() + + /// Called after remove animation. + func removeAnimationDidEnd(_ finished: Bool) + +} + +public extension CustomAnimating where Self: UIView { + + var springDamping: CGFloat { + return 0.8 + } + + func animateInsert() { } + + func insertAnimationWillBegin() { } + + func insertAnimationDidEnd(_ finished: Bool) { } + + func animateRemove() { } + + func removeAnimationWillBegin() { } + + func removeAnimationDidEnd(_ finished: Bool) { } + +}