Merge branch 'master' into feature/TINetworking
|
|
@ -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.
|
||||
|
|
|
|||
2
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
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -22,6 +22,6 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public protocol BaseViewModel: class {
|
||||
public protocol BaseViewModel: AnyObject {
|
||||
// Nothing
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
//
|
||||
|
||||
/// PaginationWrapper delegate used for pagination results handling
|
||||
public protocol PaginationWrapperDelegate: class {
|
||||
public protocol PaginationWrapperDelegate: AnyObject {
|
||||
|
||||
associatedtype DataSourceType: DataSource
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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' }
|
||||
|
|
|
|||
|
|
@ -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' }
|
||||
|
|
|
|||
|
|
@ -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>)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
//
|
||||
|
||||
/// PaginationWrapper delegate used for pagination results handling
|
||||
public protocol PaginatorDelegate: class {
|
||||
public protocol PaginatorDelegate: AnyObject {
|
||||
|
||||
associatedtype Page
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
public protocol PaginatorUIDelegate: class {
|
||||
public protocol PaginatorUIDelegate: AnyObject {
|
||||
|
||||
associatedtype ErrorType
|
||||
|
||||
|
|
|
|||
|
|
@ -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' }
|
||||
|
|
|
|||
|
|
@ -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' }
|
||||
|
|
|
|||
|
|
@ -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' }
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 244 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 796 KiB |
|
After Width: | Height: | Size: 703 KiB |
|
After Width: | Height: | Size: 801 KiB |
|
After Width: | Height: | Size: 747 KiB |
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import UIKit
|
||||
|
||||
public protocol TransitioningHandler: UIScrollViewDelegate {
|
||||
var animator: CollapsibleViewsAnimator? { get set }
|
||||
|
||||
init(collapsibleViewsContainer: CollapsibleViewsContainer)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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' }
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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' }
|
||||
|
|
|
|||