Last active
August 1, 2018 22:36
-
-
Save jakehawken/6c9f1716c4848c88be222d50b03886bd to your computer and use it in GitHub Desktop.
A way to easily transition between displaying two different sets of subviews in a UIStackView.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Foundation | |
| import UIKit | |
| class ToggleableStackView: UIStackView { | |
| enum Cohort { | |
| case primary | |
| case secondary | |
| var next: Cohort { | |
| switch self { | |
| case .primary: | |
| return .secondary | |
| case .secondary: | |
| return .primary | |
| } | |
| } | |
| } | |
| typealias TransitionCompletion = ()->() | |
| internal var fadeSpeed: TimeInterval = 0.2 | |
| private(set) var isAnimating: Bool = false | |
| private(set) var currentCohort: Cohort? | |
| private var primaryCohort: Set<UIView>? | |
| internal func setPrimaryCohort(_ cohort: [UIView]) { | |
| let newPrimary: Set<UIView> = Set(cohort) | |
| guard allAreLocalSubviews(newPrimary) else { | |
| fatalError("All views in a cohort must be subviews of the stackview.") | |
| } | |
| primaryCohort = newPrimary | |
| } | |
| internal func toggle(animated: Bool = true, callback: TransitionCompletion? = nil) { | |
| guard let current = currentCohort else { | |
| return | |
| } | |
| switchTo(cohort: current.next, animate: animated, callback: callback) | |
| } | |
| internal func switchTo(cohort: Cohort, animate: Bool, callback: TransitionCompletion? = nil) { | |
| guard isAnimating == false else { | |
| return | |
| } | |
| guard let primary = primaryCohort else { | |
| return | |
| } | |
| // Secondary is all subviews that are not in primary. | |
| let secondary = Set(subviews.filter { !primary.contains($0) }) | |
| if animate == false { | |
| switchCohortsInstantly(primary: primary, secondary: secondary, cohort: cohort, callback: callback) | |
| } | |
| else { | |
| switchCohortsAnimated(primary: primary, secondary: secondary, cohort: cohort, callback: callback) | |
| } | |
| } | |
| //MARK: private | |
| private func allAreLocalSubviews(_ set: Set<UIView>) -> Bool { | |
| let localSet: Set<UIView> = Set(subviews) | |
| return set.isSubset(of: localSet) | |
| } | |
| private func switchCohortsInstantly(primary: Set<UIView>, secondary: Set<UIView>, cohort: Cohort, callback: TransitionCompletion?) { | |
| let hidePrimary = (cohort == .secondary) | |
| primary.forEach { $0.alpha = hidePrimary ? 0 : 1 } | |
| primary.forEach { $0.isHidden = hidePrimary } | |
| secondary.forEach { $0.alpha = hidePrimary ? 1 : 0 } | |
| secondary.forEach { $0.isHidden = !hidePrimary } | |
| currentCohort = cohort | |
| callback?() | |
| } | |
| private func switchCohortsAnimated(primary: Set<UIView>, secondary: Set<UIView>, cohort: Cohort, callback: TransitionCompletion?) { | |
| isAnimating = true | |
| let hidePrimary = (cohort == .secondary) | |
| //Makes sure that you're not animating the alpha of a hidden view. | |
| let cohortToFadeOut = hidePrimary ? primary : secondary | |
| let cohortToFadeIn = hidePrimary ? secondary : primary | |
| fadeOutAndHide(views: cohortToFadeOut) { [weak self] in | |
| self?.unhideAndFadeIn(views: cohortToFadeIn) { | |
| self?.currentCohort = cohort | |
| self?.isAnimating = false | |
| callback?() | |
| } | |
| } | |
| } | |
| private func fadeOutAndHide(views: Set<UIView>, transitionCompletion: @escaping TransitionCompletion) { | |
| UIView.animate(withDuration: fadeSpeed, animations: { | |
| views.forEach { $0.alpha = 0 } | |
| }) { (_) in | |
| views.forEach { $0.isHidden = true } | |
| transitionCompletion() | |
| } | |
| } | |
| private func unhideAndFadeIn(views: Set<UIView>, transitionCompletion: @escaping TransitionCompletion) { | |
| views.forEach { | |
| $0.alpha = 0 | |
| $0.isHidden = false | |
| } | |
| UIView.animate(withDuration: fadeSpeed, animations: { | |
| views.forEach { $0.alpha = 1 } | |
| }) { (_) in | |
| transitionCompletion() | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment