diff --git a/CHANGELOG.md b/CHANGELOG.md index ab21c1ee..1501693d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +### 1.5.0 +- **Add**: `HeaderTransitionDelegate` - Helper for transition of TableView header and navigationBar title view + ### 1.4.0 - **Update**: update minor dependencies. - **Fix**: project's scripts. diff --git a/Cartfile b/Cartfile index ff4698d3..caf22909 100644 --- a/Cartfile +++ b/Cartfile @@ -3,5 +3,5 @@ github "Alamofire/Alamofire" github "RxSwiftCommunity/RxAlamofire" ~> 6.1 github "TouchInstinct/TableKit" github "ReactiveX/RxSwift" ~> 6.2 -github "pronebird/UIScrollView-InfiniteScroll" +github "pronebird/UIScrollView-InfiniteScroll" "1.1.0" github "SnapKit/SnapKit" ~> 5.0 \ No newline at end of file diff --git a/Cartfile.resolved b/Cartfile.resolved index 2df82d97..7bbac131 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -4,4 +4,4 @@ github "RxSwiftCommunity/RxAlamofire" "v6.1.2" github "SnapKit/SnapKit" "5.0.1" github "TouchInstinct/TableKit" "2.10008.1" github "malcommac/SwiftDate" "6.3.1" -github "pronebird/UIScrollView-InfiniteScroll" "1.2.0" +github "pronebird/UIScrollView-InfiniteScroll" "1.1.0" diff --git a/LeadKit.podspec b/LeadKit.podspec index a5c1cbfe..526e2d80 100644 --- a/LeadKit.podspec +++ b/LeadKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LeadKit" - s.version = "1.4.0" + s.version = "1.5.1" s.summary = "iOS framework with a bunch of tools for rapid development" s.homepage = "https://github.com/TouchInstinct/LeadKit" s.license = "Apache License, Version 2.0" @@ -107,9 +107,9 @@ Pod::Spec.new do |s| "Sources/Classes/Controllers/BaseOrientationController.swift" ] - ss.dependency "RxSwift", '~> 6.0.0' - ss.dependency "RxCocoa", '~> 6.0.0' - ss.dependency "RxAlamofire", '~> 6.1.1' + ss.dependency "RxSwift", '~> 6.2' + ss.dependency "RxCocoa", '~> 6.2' + ss.dependency "RxAlamofire", '~> 6.1' ss.dependency "SwiftDate", '~> 6' ss.ios.dependency "TableKit", '~> 2.11' diff --git a/Sources/Protocols/BaseViewModel.swift b/Sources/Protocols/BaseViewModel.swift index 948097fe..280ef174 100644 --- a/Sources/Protocols/BaseViewModel.swift +++ b/Sources/Protocols/BaseViewModel.swift @@ -22,6 +22,6 @@ import Foundation -public protocol BaseViewModel: class { +public protocol BaseViewModel: AnyObject { // Nothing } diff --git a/Sources/Protocols/DataLoading/GeneralDataLoading/GeneralDataLoadingController.swift b/Sources/Protocols/DataLoading/GeneralDataLoading/GeneralDataLoadingController.swift index e2a2ee99..a1022c83 100644 --- a/Sources/Protocols/DataLoading/GeneralDataLoading/GeneralDataLoadingController.swift +++ b/Sources/Protocols/DataLoading/GeneralDataLoading/GeneralDataLoadingController.swift @@ -20,7 +20,7 @@ // THE SOFTWARE. // -public protocol GeneralDataLoadingController: class, ConfigurableController, GeneralDataLoadingHandler +public protocol GeneralDataLoadingController: AnyObject, ConfigurableController, GeneralDataLoadingHandler where ViewModelT: GeneralDataLoadingViewModel { /// The loading view is shown when the `onLoadingState` method gets called diff --git a/Sources/Protocols/DataLoading/PaginationDataLoading/TotalCountCursorConfiguration.swift b/Sources/Protocols/DataLoading/PaginationDataLoading/TotalCountCursorConfiguration.swift index dbca27c0..2dcaf037 100644 --- a/Sources/Protocols/DataLoading/PaginationDataLoading/TotalCountCursorConfiguration.swift +++ b/Sources/Protocols/DataLoading/PaginationDataLoading/TotalCountCursorConfiguration.swift @@ -24,6 +24,6 @@ import RxSwift /// Protocol that requests class to conform RxDataSource and ResettableType /// with constraint ResultType to TotalCountCursorListingResult. -public protocol TotalCountCursorConfiguration: class, RxDataSource, ResettableType +public protocol TotalCountCursorConfiguration: AnyObject, RxDataSource, ResettableType where ResultType: TotalCountCursorListingResult { } diff --git a/Sources/Protocols/LoadingIndicator.swift b/Sources/Protocols/LoadingIndicator.swift index c4b6ccf4..724962e4 100644 --- a/Sources/Protocols/LoadingIndicator.swift +++ b/Sources/Protocols/LoadingIndicator.swift @@ -23,7 +23,7 @@ import UIKit /// Protocol that describes placeholder view, containing loading indicator. -public protocol LoadingIndicatorHolder: class { +public protocol LoadingIndicatorHolder: AnyObject { var loadingIndicator: Animatable { get } var indicatorOwner: UIView { get } } diff --git a/Sources/Protocols/Views/ViewTextConfigurable/ViewTextConfigurable.swift b/Sources/Protocols/Views/ViewTextConfigurable/ViewTextConfigurable.swift index 45b577f8..49ef2577 100644 --- a/Sources/Protocols/Views/ViewTextConfigurable/ViewTextConfigurable.swift +++ b/Sources/Protocols/Views/ViewTextConfigurable/ViewTextConfigurable.swift @@ -24,7 +24,7 @@ import UIKit.UIFont import UIKit.UIColor /// Protocol that represents text object with appearance attributes. -public protocol ViewTextConfigurable: class { +public protocol ViewTextConfigurable: AnyObject { /// Font of text object. var textFont: UIFont? { get set } diff --git a/Sources/Structures/DataLoading/PaginationDataLoading/PaginationWrapperDelegate.swift b/Sources/Structures/DataLoading/PaginationDataLoading/PaginationWrapperDelegate.swift index c33bde08..88039c8d 100644 --- a/Sources/Structures/DataLoading/PaginationDataLoading/PaginationWrapperDelegate.swift +++ b/Sources/Structures/DataLoading/PaginationDataLoading/PaginationWrapperDelegate.swift @@ -21,7 +21,7 @@ // /// PaginationWrapper delegate used for pagination results handling -public protocol PaginationWrapperDelegate: class { +public protocol PaginationWrapperDelegate: AnyObject { associatedtype DataSourceType: DataSource diff --git a/Sources/Structures/DataLoading/PaginationDataLoading/PaginationWrapperUIDelegate.swift b/Sources/Structures/DataLoading/PaginationDataLoading/PaginationWrapperUIDelegate.swift index 2d004320..3bad0367 100644 --- a/Sources/Structures/DataLoading/PaginationDataLoading/PaginationWrapperUIDelegate.swift +++ b/Sources/Structures/DataLoading/PaginationDataLoading/PaginationWrapperUIDelegate.swift @@ -22,7 +22,7 @@ /// PaginationWrapper UI delegate used for customization /// of bound states (loading, empty, error, etc.). -public protocol PaginationWrapperUIDelegate: class { +public protocol PaginationWrapperUIDelegate: AnyObject { /// Returns placeholder view for empty state. /// diff --git a/TIFoundationUtils/TIFoundationUtils.podspec b/TIFoundationUtils/TIFoundationUtils.podspec index 6e962c2f..90ecabc9 100644 --- a/TIFoundationUtils/TIFoundationUtils.podspec +++ b/TIFoundationUtils/TIFoundationUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIFoundationUtils' - s.version = '1.4.0' + s.version = '1.5.1' s.summary = 'Set of helpers for Foundation framework classes.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIKeychainUtils/TIKeychainUtils.podspec b/TIKeychainUtils/TIKeychainUtils.podspec index 7f5fe394..567c34eb 100644 --- a/TIKeychainUtils/TIKeychainUtils.podspec +++ b/TIKeychainUtils/TIKeychainUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIKeychainUtils' - s.version = '1.4.0' + s.version = '1.5.1' s.summary = 'Set of helpers for Keychain classes.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIPagination/Sources/Protocols/InfiniteScrollDelegate.swift b/TIPagination/Sources/Protocols/InfiniteScrollDelegate.swift index 07fb6838..08c1ebd2 100644 --- a/TIPagination/Sources/Protocols/InfiniteScrollDelegate.swift +++ b/TIPagination/Sources/Protocols/InfiniteScrollDelegate.swift @@ -23,7 +23,7 @@ import UIKit import TISwiftUtils -public protocol InfiniteScrollDelegate: class { +public protocol InfiniteScrollDelegate: AnyObject { func beginInfiniteScroll(_ forceScroll: Bool) func addInfiniteScroll(handler: @escaping ParameterClosure) diff --git a/TIPagination/Sources/Protocols/PaginatorDelegate.swift b/TIPagination/Sources/Protocols/PaginatorDelegate.swift index 1c21d41a..0427c9de 100644 --- a/TIPagination/Sources/Protocols/PaginatorDelegate.swift +++ b/TIPagination/Sources/Protocols/PaginatorDelegate.swift @@ -21,7 +21,7 @@ // /// PaginationWrapper delegate used for pagination results handling -public protocol PaginatorDelegate: class { +public protocol PaginatorDelegate: AnyObject { associatedtype Page diff --git a/TIPagination/Sources/Protocols/PaginatorUIDelegate.swift b/TIPagination/Sources/Protocols/PaginatorUIDelegate.swift index d5ae6811..385ccff3 100644 --- a/TIPagination/Sources/Protocols/PaginatorUIDelegate.swift +++ b/TIPagination/Sources/Protocols/PaginatorUIDelegate.swift @@ -20,7 +20,7 @@ // THE SOFTWARE. // -public protocol PaginatorUIDelegate: class { +public protocol PaginatorUIDelegate: AnyObject { associatedtype ErrorType diff --git a/TISwiftUtils/TISwiftUtils.podspec b/TISwiftUtils/TISwiftUtils.podspec index d1d3e530..7e7e8493 100644 --- a/TISwiftUtils/TISwiftUtils.podspec +++ b/TISwiftUtils/TISwiftUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TISwiftUtils' - s.version = '1.4.0' + s.version = '1.5.1' s.summary = 'Bunch of useful helpers for Swift development.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TITableKitUtils/TITableKitUtils.podspec b/TITableKitUtils/TITableKitUtils.podspec index d676eece..cdcfa5e8 100644 --- a/TITableKitUtils/TITableKitUtils.podspec +++ b/TITableKitUtils/TITableKitUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TITableKitUtils' - s.version = '1.4.0' + s.version = '1.5.1' s.summary = 'Set of helpers for TableKit classes.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TITransitions/TITransitions.podspec b/TITransitions/TITransitions.podspec index 29df4ff9..561c6a75 100644 --- a/TITransitions/TITransitions.podspec +++ b/TITransitions/TITransitions.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TITransitions' - s.version = '1.4.0' + s.version = '1.5.1' s.summary = 'Set of custom transitions to present controller. ' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIUIElements/Assets/first_header_transition_example.gif b/TIUIElements/Assets/first_header_transition_example.gif new file mode 100644 index 00000000..69f3606f Binary files /dev/null and b/TIUIElements/Assets/first_header_transition_example.gif differ diff --git a/TIUIElements/Assets/licard_header_transition_example.gif b/TIUIElements/Assets/licard_header_transition_example.gif new file mode 100644 index 00000000..e7747ace Binary files /dev/null and b/TIUIElements/Assets/licard_header_transition_example.gif differ diff --git a/TIUIElements/Assets/onlyParalax.gif b/TIUIElements/Assets/onlyParalax.gif new file mode 100644 index 00000000..8abb1c0a Binary files /dev/null and b/TIUIElements/Assets/onlyParalax.gif differ diff --git a/TIUIElements/Assets/paralaxWithScale.gif b/TIUIElements/Assets/paralaxWithScale.gif new file mode 100644 index 00000000..4555aa7b Binary files /dev/null and b/TIUIElements/Assets/paralaxWithScale.gif differ diff --git a/TIUIElements/Assets/paralaxWithTransition.gif b/TIUIElements/Assets/paralaxWithTransition.gif new file mode 100644 index 00000000..dd6fcf34 Binary files /dev/null and b/TIUIElements/Assets/paralaxWithTransition.gif differ diff --git a/TIUIElements/Assets/scale.gif b/TIUIElements/Assets/scale.gif new file mode 100644 index 00000000..db341183 Binary files /dev/null and b/TIUIElements/Assets/scale.gif differ diff --git a/TIUIElements/Assets/transition.gif b/TIUIElements/Assets/transition.gif new file mode 100644 index 00000000..cab13728 Binary files /dev/null and b/TIUIElements/Assets/transition.gif differ diff --git a/TIUIElements/README.md b/TIUIElements/README.md index 68d1da24..0bafa5af 100644 --- a/TIUIElements/README.md +++ b/TIUIElements/README.md @@ -4,6 +4,129 @@ Bunch of useful protocols and views: - `RefreshControl` - a basic UIRefreshControl with fixed refresh action. +# HeaderTransitionDelegate +Use for transition table header to navigationBar view while scrolling + +## Your class must implement CollapsibleViewsContainer protocol + +## HeaderViewHandlerProtocol +```swift +public protocol CollapsibleViewsContainer { + var topHeaderView: UIView? { get } // titleView + var bottomHeaderView: UIView? { get } // tableHeaderView + + var fixedTopOffet: CGFloat { get } // status bar + nav bar height +} +``` + +UIViewController have default realization for fixedTopOffet. + +## TransitioningHandler +TransitioningHandler Binds animators to the container. +```swift +public protocol TransitioningHandler: UIScrollViewDelegate { + var animator: CollapsibleViewsAnimator? { get set } + + init(collapsibleViewsContainer: CollapsibleViewsContainer) +} +``` + +## Customization +You can use both the various types of animation already implemented to change the view, or create your own. To create an animator, you need to implement the CollapsibleViewsAnimator protocol. +```swift +public protocol CollapsibleViewsAnimator { + var fractionComplete: CGFloat { get set } // progress of animation + var currentContentOffset: CGPoint { get set } // offset on content in table view/collection view or plain scroll view +} +``` + +Already implemented animators + +- `ParalaxAnimator` - applies only parallax effect to the header of table +- `ParalaxWithTransitionAnimator` - applies parallax effect to the header of table with transition effect down up of the navigationBar titleView +- `TransitionAnimator` - applies only transition effect down up of the navigationBar titleView +- `ScaleAnimator` - applies only scale effect down up of the navigationBar titleView +- `ParalaxWithScaleAnimator` - applies parallax effect to the header of table with scale effect down up of the navigationBar titleView +- `nil`(default value) - dont applies any effects + +TableViewHeaderTransitioningHandler is the default implementation for TransitioningHandler. It creates an animation action when scrolling the table. + +## Usage default realization with tableView +```swift +class ViewController: UITableViewController, CollapsibleViewsContainer { + private lazy var handler = TableViewHeaderTransitioningHandler(collapsibleViewsContainer: self) + + private lazy var parallaxTableHeaderView = ParallaxTableHeaderView(wrappedView: bottomHeaderView ?? UIView()) + + var topHeaderView = SomeCustomTopView() + + var bottomHeaderView = SomeCustomBottomView() + + func addViews() { + tableView.tableHeaderView = parallaxTableHeaderView + navigationController?.navigationBar.topItem?.titleView = topHeaderView + } + + func bindViews() { + handler.animator = ParalaxWithTransitionAnimator(tableHeaderView: parallaxTableHeaderView, + navBar: navigationController?.navigationBar, + currentContentOffset: tableView.contentOffset) + + tableView.delegate = self + tableView.dataSource = self + } + + override func scrollViewDidScroll(_ scrollView: UIScrollView) { + handler.scrollViewDidScroll(scrollView) + } +} + +``` + +## Examples +#### **none** + + + + + + + +
+

+ +

+
+

+ +

+
+ +#### **onlyParalax** +

+ +

+ +#### **paralaxWithTransition** +

+ +

+ +#### **transition** +

+ +

+ +#### **scale** +

+ +

+ +#### **paralaxWithScale** +

+ +

+ # Installation via SPM You can install this framework as a target of LeadKit. diff --git a/TIUIElements/Sources/Helpers/DefaultAnimators/ParalaxAnimator.swift b/TIUIElements/Sources/Helpers/DefaultAnimators/ParalaxAnimator.swift new file mode 100644 index 00000000..e972bbbe --- /dev/null +++ b/TIUIElements/Sources/Helpers/DefaultAnimators/ParalaxAnimator.swift @@ -0,0 +1,26 @@ +import UIKit + +final public class ParalaxAnimator: CollapsibleViewsAnimator { + public var fractionComplete: CGFloat = 0 { + didSet { + navBar?.topItem?.titleView?.alpha = fractionComplete == 1 ? 1 : 0 + } + } + + public var currentContentOffset: CGPoint { + didSet { + tableHeaderView?.layout(for: currentContentOffset) + } + } + + private weak var navBar: UINavigationBar? + private weak var tableHeaderView: ParallaxTableHeaderView? + + public init(tableHeaderView: ParallaxTableHeaderView, + navBar: UINavigationBar? = nil, // if nil - no alpha animation + currentContentOffset: CGPoint) { + self.currentContentOffset = currentContentOffset + self.tableHeaderView = tableHeaderView + self.navBar = navBar + } +} diff --git a/TIUIElements/Sources/Helpers/DefaultAnimators/ParalaxWithScaleAnimator.swift b/TIUIElements/Sources/Helpers/DefaultAnimators/ParalaxWithScaleAnimator.swift new file mode 100644 index 00000000..7ab2933b --- /dev/null +++ b/TIUIElements/Sources/Helpers/DefaultAnimators/ParalaxWithScaleAnimator.swift @@ -0,0 +1,28 @@ +import UIKit + +final public class ParalaxWithScaleAnimator: CollapsibleViewsAnimator { + private let paralaxAnimator: ParalaxAnimator + private let scaleAnimator: ScaleAnimator + + public var fractionComplete: CGFloat = 0 { + didSet { + paralaxAnimator.fractionComplete = fractionComplete + scaleAnimator.fractionComplete = fractionComplete + } + } + + public var currentContentOffset: CGPoint { + didSet { + paralaxAnimator.currentContentOffset = currentContentOffset + scaleAnimator.currentContentOffset = currentContentOffset + } + } + + public init(tableHeaderView: ParallaxTableHeaderView, navBar: UINavigationBar? = nil, currentContentOffset: CGPoint) { + paralaxAnimator = ParalaxAnimator(tableHeaderView: tableHeaderView, + navBar: nil, + currentContentOffset: currentContentOffset) + scaleAnimator = ScaleAnimator(navBar: navBar) + self.currentContentOffset = currentContentOffset + } +} diff --git a/TIUIElements/Sources/Helpers/DefaultAnimators/ParalaxWithTransitionAnimator .swift b/TIUIElements/Sources/Helpers/DefaultAnimators/ParalaxWithTransitionAnimator .swift new file mode 100644 index 00000000..200d9186 --- /dev/null +++ b/TIUIElements/Sources/Helpers/DefaultAnimators/ParalaxWithTransitionAnimator .swift @@ -0,0 +1,30 @@ +import UIKit + +final public class ParalaxWithTransitionAnimator: CollapsibleViewsAnimator { + private let paralaxAnimator: ParalaxAnimator + private let transitionAnimator: TransitionAnimator + + public var fractionComplete: CGFloat = 0 { + didSet { + paralaxAnimator.fractionComplete = fractionComplete + transitionAnimator.fractionComplete = fractionComplete + } + } + + public var currentContentOffset: CGPoint { + didSet { + paralaxAnimator.currentContentOffset = currentContentOffset + transitionAnimator.currentContentOffset = currentContentOffset + } + } + + public init(tableHeaderView: ParallaxTableHeaderView, + navBar: UINavigationBar? = nil, + currentContentOffset: CGPoint) { + paralaxAnimator = ParalaxAnimator(tableHeaderView: tableHeaderView, + navBar: nil, + currentContentOffset: currentContentOffset) + transitionAnimator = TransitionAnimator(navBar: navBar) + self.currentContentOffset = currentContentOffset + } +} diff --git a/TIUIElements/Sources/Helpers/DefaultAnimators/ScaleAnimator.swift b/TIUIElements/Sources/Helpers/DefaultAnimators/ScaleAnimator.swift new file mode 100644 index 00000000..b574d232 --- /dev/null +++ b/TIUIElements/Sources/Helpers/DefaultAnimators/ScaleAnimator.swift @@ -0,0 +1,19 @@ +import UIKit + +final public class ScaleAnimator: CollapsibleViewsAnimator { + public var fractionComplete: CGFloat = 0 { + didSet { + navBar?.topItem?.titleView?.scale(to: fractionComplete) + } + } + + public var currentContentOffset = CGPoint.zero + + private weak var navBar: UINavigationBar? + + public init(navBar: UINavigationBar? = nil) { + self.navBar = navBar + navBar?.topItem?.titleView?.alpha = 0 + navBar?.topItem?.titleView?.transform = CGAffineTransform(scaleX: -0.5, y: 0.5) + } +} diff --git a/TIUIElements/Sources/Helpers/DefaultAnimators/TransitionAnimator.swift b/TIUIElements/Sources/Helpers/DefaultAnimators/TransitionAnimator.swift new file mode 100644 index 00000000..33b631ca --- /dev/null +++ b/TIUIElements/Sources/Helpers/DefaultAnimators/TransitionAnimator.swift @@ -0,0 +1,18 @@ +import UIKit + +final public class TransitionAnimator: CollapsibleViewsAnimator { + public var fractionComplete: CGFloat = 0 { + didSet { + navBar?.topItem?.titleView?.transition(to: fractionComplete) + } + } + + public var currentContentOffset = CGPoint.zero + + private weak var navBar: UINavigationBar? + + public init(navBar: UINavigationBar? = nil) { + navBar?.topItem?.titleView?.alpha = 0 + self.navBar = navBar + } +} diff --git a/TIUIElements/Sources/Helpers/Extensions/UIView+Animate.swift b/TIUIElements/Sources/Helpers/Extensions/UIView+Animate.swift new file mode 100644 index 00000000..a580df4e --- /dev/null +++ b/TIUIElements/Sources/Helpers/Extensions/UIView+Animate.swift @@ -0,0 +1,17 @@ +import UIKit + +public extension UIView { + func transition(to coefficient: CGFloat) { + UIView.animate(withDuration: 0.2) { [weak self] in + self?.alpha = coefficient + self?.transform = CGAffineTransform(translationX: 0, y: -coefficient*10) + } + } + + func scale(to coefficient: CGFloat) { + UIView.animate(withDuration: 0.2){ [weak self] in + self?.alpha = coefficient + self?.transform = CGAffineTransform(scaleX: coefficient, y: coefficient) + } + } +} diff --git a/TIUIElements/Sources/Helpers/Extensions/UIViewController+FixedTopOffet.swift b/TIUIElements/Sources/Helpers/Extensions/UIViewController+FixedTopOffet.swift new file mode 100644 index 00000000..a370590b --- /dev/null +++ b/TIUIElements/Sources/Helpers/Extensions/UIViewController+FixedTopOffet.swift @@ -0,0 +1,23 @@ +import UIKit + +public extension UIViewController { + + var fixedTopOffet: CGFloat { // status bar + nav bar height calculate for UIViewController + let navigationBar = navigationController?.navigationBar + let window = view.window + let prefersLargeTitles = navigationBar?.prefersLargeTitles ?? false + + let statusBarHeight: CGFloat + if #available(iOS 13.0, *) { + statusBarHeight = window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0 + } else { + statusBarHeight = UIApplication.shared.statusBarFrame.height + } + + let navigationBarHeight = navigationBar?.bounds.height ?? 0 + + return prefersLargeTitles + ? navigationBarHeight - statusBarHeight + : 0 + } +} diff --git a/TIUIElements/Sources/Helpers/Protocols/CollapsibleViewsAnimator.swift b/TIUIElements/Sources/Helpers/Protocols/CollapsibleViewsAnimator.swift new file mode 100644 index 00000000..fe0ddcbd --- /dev/null +++ b/TIUIElements/Sources/Helpers/Protocols/CollapsibleViewsAnimator.swift @@ -0,0 +1,6 @@ +import UIKit + +public protocol CollapsibleViewsAnimator { + var fractionComplete: CGFloat { get set } // progress of animation + var currentContentOffset: CGPoint { get set } // offset on content in table view/collection view or plain scroll view +} diff --git a/TIUIElements/Sources/Helpers/Protocols/CollapsibleViewsContainer.swift b/TIUIElements/Sources/Helpers/Protocols/CollapsibleViewsContainer.swift new file mode 100644 index 00000000..9bb97810 --- /dev/null +++ b/TIUIElements/Sources/Helpers/Protocols/CollapsibleViewsContainer.swift @@ -0,0 +1,8 @@ +import UIKit + +public protocol CollapsibleViewsContainer { + var topHeaderView: UIView? { get } // titleView + var bottomHeaderView: UIView? { get } // tableHeaderView + + var fixedTopOffet: CGFloat { get } // status bar + nav bar height +} diff --git a/TIUIElements/Sources/Helpers/Protocols/TransitioningHandler.swift b/TIUIElements/Sources/Helpers/Protocols/TransitioningHandler.swift new file mode 100644 index 00000000..bb3b7121 --- /dev/null +++ b/TIUIElements/Sources/Helpers/Protocols/TransitioningHandler.swift @@ -0,0 +1,7 @@ +import UIKit + +public protocol TransitioningHandler: UIScrollViewDelegate { + var animator: CollapsibleViewsAnimator? { get set } + + init(collapsibleViewsContainer: CollapsibleViewsContainer) +} diff --git a/TIUIElements/Sources/Helpers/TableViewHeaderTransitioningHandler.swift b/TIUIElements/Sources/Helpers/TableViewHeaderTransitioningHandler.swift new file mode 100644 index 00000000..3ca66be2 --- /dev/null +++ b/TIUIElements/Sources/Helpers/TableViewHeaderTransitioningHandler.swift @@ -0,0 +1,34 @@ +import UIKit + +final class TableViewHeaderTransitioningHandler: NSObject, TransitioningHandler { + var animator: CollapsibleViewsAnimator? + + private var startOffset: CGFloat = 0 + private var navigationBarOffset: CGFloat = 0 + private var isFirstScroll = true + + private var container: CollapsibleViewsContainer? + + init(collapsibleViewsContainer: CollapsibleViewsContainer) { + self.container = collapsibleViewsContainer + } + + public func scrollViewDidScroll(_ scrollView: UIScrollView) { + guard let largeHeaderView = container?.bottomHeaderView else { + return + } + + if isFirstScroll { + startOffset = max(-(scrollView.contentOffset.y), 0) + navigationBarOffset = container?.fixedTopOffet ?? 0 + isFirstScroll = false + } + + let offsetY = scrollView.contentOffset.y + startOffset + + let alpha = min(offsetY / (largeHeaderView.frame.height + navigationBarOffset), 1) + + animator?.fractionComplete = alpha + animator?.currentContentOffset = scrollView.contentOffset + } +} diff --git a/TIUIElements/Sources/Helpers/Views/ParallaxTableHeaderView.swift b/TIUIElements/Sources/Helpers/Views/ParallaxTableHeaderView.swift new file mode 100644 index 00000000..803ce013 --- /dev/null +++ b/TIUIElements/Sources/Helpers/Views/ParallaxTableHeaderView.swift @@ -0,0 +1,44 @@ +import Foundation +import UIKit + +open class ParallaxTableHeaderView: UIView { + private var container: UIView + + public init(wrappedView: UIView) { + container = UIView(frame: wrappedView.frame) + super.init(frame: CGRect(x: 0, + y: 0, + width: wrappedView.frame.size.width, + height: wrappedView.frame.size.height)) + wrappedView.autoresizingMask = [ + .flexibleLeftMargin, + .flexibleRightMargin, + .flexibleTopMargin, + .flexibleBottomMargin, + .flexibleHeight, + .flexibleWidth + ] + container.addSubview(wrappedView) + addSubview(container) + + clipsToBounds = false + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + open func layout(for contentOffset: CGPoint) { + guard contentOffset.y <= 0 else { + return + } + + let delta: CGFloat = abs(contentOffset.y) + var rect = frame + rect.origin = .zero + rect.origin.y -= delta + rect.size.height += delta + container.frame = rect + } +} diff --git a/TIUIElements/TIUIElements.podspec b/TIUIElements/TIUIElements.podspec index 571fcaca..26a60423 100644 --- a/TIUIElements/TIUIElements.podspec +++ b/TIUIElements/TIUIElements.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIUIElements' - s.version = '1.4.0' + s.version = '1.5.1' s.summary = 'Bunch of useful protocols and views.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIUIKitCore/Sources/Protocols/ActivityIndicator/ActivityIndicatorHolder.swift b/TIUIKitCore/Sources/Protocols/ActivityIndicator/ActivityIndicatorHolder.swift index dffd4b32..8865d21a 100644 --- a/TIUIKitCore/Sources/Protocols/ActivityIndicator/ActivityIndicatorHolder.swift +++ b/TIUIKitCore/Sources/Protocols/ActivityIndicator/ActivityIndicatorHolder.swift @@ -23,7 +23,7 @@ import UIKit /// Protocol that describes placeholder view, containing activity indicator. -public protocol ActivityIndicatorHolder: class { +public protocol ActivityIndicatorHolder: AnyObject { var activityIndicator: Animatable { get } var indicatorOwner: UIView { get } } diff --git a/TIUIKitCore/TIUIKitCore.podspec b/TIUIKitCore/TIUIKitCore.podspec index 9c653fcb..9498c4a0 100644 --- a/TIUIKitCore/TIUIKitCore.podspec +++ b/TIUIKitCore/TIUIKitCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIUIKitCore' - s.version = '1.4.0' + s.version = '1.5.1' s.summary = 'Core UI elements: protocols, views and helpers.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' }