Merge branch 'master' into feature/TINetworking

This commit is contained in:
Ivan Smolin 2021-09-14 16:47:16 +03:00
commit ff47d2c855
42 changed files with 410 additions and 24 deletions

View File

@ -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.

View File

@ -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

View File

@ -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"

View File

@ -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'

View File

@ -22,6 +22,6 @@
import Foundation
public protocol BaseViewModel: class {
public protocol BaseViewModel: AnyObject {
// Nothing
}

View File

@ -20,7 +20,7 @@
// THE SOFTWARE.
//
public protocol GeneralDataLoadingController: class, ConfigurableController, GeneralDataLoadingHandler
public protocol GeneralDataLoadingController: AnyObject, ConfigurableController, GeneralDataLoadingHandler
where ViewModelT: GeneralDataLoadingViewModel<ResultType> {
/// The loading view is shown when the `onLoadingState` method gets called

View File

@ -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 {
}

View File

@ -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 }
}

View File

@ -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 }

View File

@ -21,7 +21,7 @@
//
/// PaginationWrapper delegate used for pagination results handling
public protocol PaginationWrapperDelegate: class {
public protocol PaginationWrapperDelegate: AnyObject {
associatedtype DataSourceType: DataSource

View File

@ -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.
///

View File

@ -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' }

View File

@ -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' }

View File

@ -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<UITableView>)

View File

@ -21,7 +21,7 @@
//
/// PaginationWrapper delegate used for pagination results handling
public protocol PaginatorDelegate: class {
public protocol PaginatorDelegate: AnyObject {
associatedtype Page

View File

@ -20,7 +20,7 @@
// THE SOFTWARE.
//
public protocol PaginatorUIDelegate: class {
public protocol PaginatorUIDelegate: AnyObject {
associatedtype ErrorType

View File

@ -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' }

View File

@ -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' }

View File

@ -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' }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 747 KiB

View File

@ -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**
<table border="0" cellspacing="30" cellpadding="30">
<tbody>
<tr>
<td>
<p align="left">
<img src="Assets/first_header_transition_example.gif" width=300 height=600>
</p>
</td>
<td>
<p align="right">
<img src="Assets/licard_header_transition_example.gif" width=300 height=600>
</p>
</td>
</tr>
</tbody>
</table>
#### **onlyParalax**
<p align="left">
<img src="Assets/onlyParalax.gif" width=300 height=600>
</p>
#### **paralaxWithTransition**
<p align="left">
<img src="Assets/paralaxWithTransition.gif" width=300 height=600>
</p>
#### **transition**
<p align="left">
<img src="Assets/transition.gif" width=300 height=600>
</p>
#### **scale**
<p align="left">
<img src="Assets/scale.gif" width=300 height=600>
</p>
#### **paralaxWithScale**
<p align="left">
<img src="Assets/paralaxWithScale.gif" width=300 height=600>
</p>
# Installation via SPM
You can install this framework as a target of LeadKit.

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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)
}
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -0,0 +1,7 @@
import UIKit
public protocol TransitioningHandler: UIScrollViewDelegate {
var animator: CollapsibleViewsAnimator? { get set }
init(collapsibleViewsContainer: CollapsibleViewsContainer)
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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' }

View File

@ -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 }
}

View File

@ -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' }