Commit cae21143 by whitepixelstudios Committed by GitHub

Merge pull request #3 from CosmicMind/master

Update version
parents be45d80b 112992a8
...@@ -16,3 +16,12 @@ profile ...@@ -16,3 +16,12 @@ profile
*.moved-aside *.moved-aside
*.playground *.playground
*.framework *.framework
Sources/Frameworks/Motion/.git*
Sources/Frameworks/Motion/.gitignore
Sources/Frameworks/Motion/.swift-version
Sources/Frameworks/Motion/CONTRIBUTING.md
Sources/Frameworks/Motion/Motion.podspec
Sources/Frameworks/Motion/Motion.xcodeproj/
Sources/Frameworks/Motion/Sources/Info.plist
Sources/Frameworks/Motion/Sources/Motion.h
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'Material' s.name = 'Material'
s.version = '2.8.1' s.version = '2.9.4'
s.license = 'BSD-3-Clause' s.license = 'BSD-3-Clause'
s.summary = 'Material Design library used to create beautiful applications.' s.summary = 'A Material Design library for creating beautiful applications.'
s.homepage = 'http://materialswift.com' s.homepage = 'http://materialswift.com'
s.social_media_url = 'https://www.facebook.com/cosmicmindcom' s.social_media_url = 'https://www.facebook.com/cosmicmindcom'
s.authors = { 'CosmicMind, Inc.' => 'support@cosmicmind.com' } s.authors = { 'CosmicMind, Inc.' => 'support@cosmicmind.com' }
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "0F44C283-777E-4E3F-9E10-FC52C3C270CE", "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "0F44C283-777E-4E3F-9E10-FC52C3C270CE",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
"9CF35BC641478FBD765F7442D4034ED362261E0A" : "Material\/", "9CF35BC641478FBD765F7442D4034ED362261E0A" : "Material\/",
"0F3E254D46E5A5B90D1542854F510B7D145A9F31" : "Material\/Frameworks\/Motion\/" "0F3E254D46E5A5B90D1542854F510B7D145A9F31" : "Material\/Sources\/Frameworks\/Motion\/"
}, },
"DVTSourceControlWorkspaceBlueprintNameKey" : "Material", "DVTSourceControlWorkspaceBlueprintNameKey" : "Material",
"DVTSourceControlWorkspaceBlueprintVersion" : 204, "DVTSourceControlWorkspaceBlueprintVersion" : 204,
......
...@@ -4,14 +4,20 @@ ...@@ -4,14 +4,20 @@
Welcome to **Material,** a Material Design library used to create beautiful applications. Material's animation system has been completely reworked to take advantage of [Motion](https://github.com/CosmicMind/Motion), a library dedicated to animations and transitions. Welcome to **Material,** a Material Design library used to create beautiful applications. Material's animation system has been completely reworked to take advantage of [Motion](https://github.com/CosmicMind/Motion), a library dedicated to animations and transitions.
[![Carthage compatible](https://img.shields.io/badge/Carthage-Compatible-brightgreen.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Version](https://img.shields.io/cocoapods/v/Material.svg?style=flat)](http://cocoapods.org/pods/Material)
[![License](https://img.shields.io/cocoapods/l/Material.svg?style=flat)](https://github.com/lkzhao/Material/blob/master/LICENSE?raw=true)
![Xcode 8.2+](https://img.shields.io/badge/Xcode-8.2%2B-blue.svg)
![iOS 8.0+](https://img.shields.io/badge/iOS-8.0%2B-blue.svg)
![Swift 3.0+](https://img.shields.io/badge/Swift-3.0%2B-orange.svg)
[![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9D6MURMLLUNQ2)
## Photos Sample ## Photos Sample
Take a look at a sample [Photos](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/Photos) project to get started. Take a look at a sample [Photos](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/Photos) project to get started.
![Photos](http://www.cosmicmind.com/motion/projects/photos.gif) ![Photos](http://www.cosmicmind.com/motion/projects/photos.gif)
* [Photos Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/Photos)
## Sample Projects ## Sample Projects
Take a look at [Sample Projects](https://github.com/CosmicMind/Samples) to get your projects started. Take a look at [Sample Projects](https://github.com/CosmicMind/Samples) to get your projects started.
...@@ -27,8 +33,8 @@ Take a look at [Sample Projects](https://github.com/CosmicMind/Samples) to get y ...@@ -27,8 +33,8 @@ Take a look at [Sample Projects](https://github.com/CosmicMind/Samples) to get y
- [x] Icons - [x] Icons
- [x] TextField - [x] TextField
- [X] Snackbar - [X] Snackbar
- [x] TabBar
- [x] Tabs - [x] Tabs
- [x] Chips
- [X] SearchBar - [X] SearchBar
- [x] NavigationController - [x] NavigationController
- [x] NavigationDrawer - [x] NavigationDrawer
...@@ -150,7 +156,7 @@ A SearchBar is a powerful navigation tool that allows for user's input with an i ...@@ -150,7 +156,7 @@ A SearchBar is a powerful navigation tool that allows for user's input with an i
## Tabs ## Tabs
Tabs is a new component that links a customizable TabBar to a stack of view controllers making a powerful and visually pleasing component to have in any application. The TabBar can be aligned at the top or bottom of the view controller. Tabs is a new component that links a customizable TabBar to a stack of view controllers making a powerful and visually pleasing component to have in any application.
![Tabs](http://www.cosmicmind.com/material/white/page-tab-bar-controller.gif) ![Tabs](http://www.cosmicmind.com/material/white/page-tab-bar-controller.gif)
......
...@@ -12,13 +12,13 @@ Take a look at a sample [Photos](https://github.com/CosmicMind/Samples/tree/mast ...@@ -12,13 +12,13 @@ Take a look at a sample [Photos](https://github.com/CosmicMind/Samples/tree/mast
* [Photos Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/Photos) * [Photos Sample](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/Photos)
### Who is this for? ### Who is Motion for?
Motion is designed for beginner to expert developers. For beginners, you will be exposed to very powerful APIs that would take time and experience to develop on your own, and experts will appreciate the time saved by using Motion. Motion is designed for beginner to expert developers. For beginners, you will be exposed to very powerful APIs that would take time and experience to develop on your own, and experts will appreciate the time saved by using Motion.
### What you will learn ### What you will learn
You will learn how to use Motion with a general introduction to fundamental concepts and easy to use code snippets. You will learn how to use Motion with a general introduction to fundamental concepts and easy to use code snippets.
# Transitions # Transitions
...@@ -48,7 +48,7 @@ orangeView.motionIdentifier = "bar" ...@@ -48,7 +48,7 @@ orangeView.motionIdentifier = "bar"
orangeView.transition(.translate(x: -100)) orangeView.transition(.translate(x: -100))
``` ```
The above code snippet tells the source views in `view controller 1` to link to the destination views in `view controller 2` using the `motionIdentifier`. Animations may be added to views during a transition using the **transition** method. The *transition* method accepts MotionTransition structs that configure the view's animation. The above code snippet tells the source views in `view controller 1` to link to the destination views in `view controller 2` using the `motionIdentifier`. Animations may be added to views during a transition using the **transition** method. The *transition* method accepts MotionTransition structs that configure the view's animation.
* [MotionTransition API](https://cosmicmind.gitbooks.io/motion/content/motion_transition_api.html) * [MotionTransition API](https://cosmicmind.gitbooks.io/motion/content/motion_transition_api.html)
* [Code Samples](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/TransitionsWithIdentifier) * [Code Samples](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/TransitionsWithIdentifier)
...@@ -84,7 +84,7 @@ motionNavigationTransitionType = .autoReverse(presenting: .zoom) ...@@ -84,7 +84,7 @@ motionNavigationTransitionType = .autoReverse(presenting: .zoom)
``` ```
* [Code Samples](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/Transitions) * [Code Samples](https://github.com/CosmicMind/Samples/tree/master/Projects/Programmatic/Transitions)
# Animations # Animations
Motion provides the building blocks necessary to create stunning animations without much effort. Motion's animation API will make maintenance a breeze and changes even easier. To create an animation, use the **animate** method of a view or layer and pass in a list of MotionAnimation structs. MotionAnimation structs are configurable values that describe how to animate a property or group of properties. Motion provides the building blocks necessary to create stunning animations without much effort. Motion's animation API will make maintenance a breeze and changes even easier. To create an animation, use the **animate** method of a view or layer and pass in a list of MotionAnimation structs. MotionAnimation structs are configurable values that describe how to animate a property or group of properties.
......
...@@ -25,22 +25,22 @@ ...@@ -25,22 +25,22 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
import UIKit import UIKit
public extension CAMediaTimingFunction { public extension CAMediaTimingFunction {
// default // Default
static let linear = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear) static let linear = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
static let easeIn = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn) static let easeIn = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn)
static let easeOut = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) static let easeOut = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
static let easeInOut = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) static let easeInOut = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
// material // Material
static let standard = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 0.2, 1.0) static let standard = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 0.2, 1.0)
static let deceleration = CAMediaTimingFunction(controlPoints: 0.0, 0.0, 0.2, 1) static let deceleration = CAMediaTimingFunction(controlPoints: 0.0, 0.0, 0.2, 1)
static let acceleration = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 1, 1) static let acceleration = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 1, 1)
static let sharp = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 0.6, 1) static let sharp = CAMediaTimingFunction(controlPoints: 0.4, 0.0, 0.6, 1)
// easing.net // Easing.net
static let easeOutBack = CAMediaTimingFunction(controlPoints: 0.175, 0.885, 0.32, 1.75) static let easeOutBack = CAMediaTimingFunction(controlPoints: 0.175, 0.885, 0.32, 1.75)
} }
...@@ -31,7 +31,7 @@ internal struct AssociatedObject { ...@@ -31,7 +31,7 @@ internal struct AssociatedObject {
- Parameter initializer: Object initializer. - Parameter initializer: Object initializer.
- Returns: The associated reference for the initializer object. - Returns: The associated reference for the initializer object.
*/ */
public static func get<T: Any>(base: Any, key: UnsafePointer<UInt8>, initializer: () -> T) -> T { static func get<T: Any>(base: Any, key: UnsafePointer<UInt8>, initializer: () -> T) -> T {
if let v = objc_getAssociatedObject(base, key) as? T { if let v = objc_getAssociatedObject(base, key) as? T {
return v return v
} }
...@@ -48,7 +48,7 @@ internal struct AssociatedObject { ...@@ -48,7 +48,7 @@ internal struct AssociatedObject {
- Parameter value: The object instance to set for the associated object. - Parameter value: The object instance to set for the associated object.
- Returns: The associated reference for the initializer object. - Returns: The associated reference for the initializer object.
*/ */
public static func set<T: Any>(base: Any, key: UnsafePointer<UInt8>, value: T) { static func set<T: Any>(base: Any, key: UnsafePointer<UInt8>, value: T) {
objc_setAssociatedObject(base, key, value, .OBJC_ASSOCIATION_RETAIN) objc_setAssociatedObject(base, key, value, .OBJC_ASSOCIATION_RETAIN)
} }
} }
...@@ -111,13 +111,13 @@ public protocol MotionViewControllerDelegate { ...@@ -111,13 +111,13 @@ public protocol MotionViewControllerDelegate {
/** /**
### The singleton class/object for controlling interactive transitions. ### The singleton class/object for controlling interactive transitions.
```swift ```swift
Motion.shared Motion.shared
``` ```
#### Use the following methods for controlling the interactive transition: #### Use the following methods for controlling the interactive transition:
```swift ```swift
func update(progress:Double) func update(progress:Double)
func end() func end()
...@@ -128,16 +128,16 @@ public protocol MotionViewControllerDelegate { ...@@ -128,16 +128,16 @@ public protocol MotionViewControllerDelegate {
public class Motion: MotionController { public class Motion: MotionController {
/// Shared singleton object for controlling the transition /// Shared singleton object for controlling the transition
public static let shared = Motion() public static let shared = Motion()
/// Source view controller. /// Source view controller.
public internal(set) var fromViewController: UIViewController? public internal(set) var fromViewController: UIViewController?
/// Destination view controller. /// Destination view controller.
public internal(set) var toViewController: UIViewController? public internal(set) var toViewController: UIViewController?
/// Whether or not we are presenting the destination view controller. /// Whether or not we are presenting the destination view controller.
public internal(set) var isPresenting = true public internal(set) var isPresenting = true
/// Progress of the current transition, 0 if a transition is not happening. /// Progress of the current transition, 0 if a transition is not happening.
public override var elapsedTime: TimeInterval { public override var elapsedTime: TimeInterval {
didSet { didSet {
...@@ -148,22 +148,22 @@ public class Motion: MotionController { ...@@ -148,22 +148,22 @@ public class Motion: MotionController {
transitionContext?.updateInteractiveTransition(CGFloat(elapsedTime)) transitionContext?.updateInteractiveTransition(CGFloat(elapsedTime))
} }
} }
/// Indicates whether the transition is animating or not. /// Indicates whether the transition is animating or not.
public var isAnimating = false public var isAnimating = false
/** /**
A UIViewControllerContextTransitioning object provided by UIKit, which A UIViewControllerContextTransitioning object provided by UIKit, which
might be nil when isTransitioning. This happens when calling motionReplaceViewController might be nil when isTransitioning. This happens when calling motionReplaceViewController
*/ */
internal weak var transitionContext: UIViewControllerContextTransitioning? internal weak var transitionContext: UIViewControllerContextTransitioning?
/// A reference to a fullscreen snapshot. /// A reference to a fullscreen snapshot.
internal var fullScreenSnapshot: UIView! internal var fullScreenSnapshot: UIView!
/// Default animation type. /// Default animation type.
internal var defaultAnimation = MotionTransitionType.auto internal var defaultAnimation = MotionTransitionType.auto
/// The color of the transitioning container. /// The color of the transitioning container.
internal var containerBackgroundColor: UIColor? internal var containerBackgroundColor: UIColor?
...@@ -173,16 +173,16 @@ public class Motion: MotionController { ...@@ -173,16 +173,16 @@ public class Motion: MotionController {
UINavigationController.setViewControllers not able to handle interactive transitions. UINavigationController.setViewControllers not able to handle interactive transitions.
*/ */
internal var forceNonInteractive = false internal var forceNonInteractive = false
/// Inserts the toViews first. /// Inserts the toViews first.
internal var insertToViewFirst = false internal var insertToViewFirst = false
/// Indicates whether a UINavigationController is transitioning. /// Indicates whether a UINavigationController is transitioning.
internal var isNavigationController = false internal var isNavigationController = false
/// Indicates whether a UITabBarController is transitioning. /// Indicates whether a UITabBarController is transitioning.
internal var isTabBarController = false internal var isTabBarController = false
/// Indicates whether a UINavigationController or UITabBarController is transitioning. /// Indicates whether a UINavigationController or UITabBarController is transitioning.
internal var isContainerController: Bool { internal var isContainerController: Bool {
return isNavigationController || isTabBarController return isNavigationController || isTabBarController
...@@ -205,7 +205,7 @@ public class Motion: MotionController { ...@@ -205,7 +205,7 @@ public class Motion: MotionController {
return !isContainerController && (.overFullScreen == v.modalPresentationStyle || .overCurrentContext == v.modalPresentationStyle) return !isContainerController && (.overFullScreen == v.modalPresentationStyle || .overCurrentContext == v.modalPresentationStyle)
} }
/// A reference to the fromView, fromViewController.view. /// A reference to the fromView, fromViewController.view.
internal var fromView: UIView? { internal var fromView: UIView? {
return fromViewController?.view return fromViewController?.view
...@@ -215,7 +215,7 @@ public class Motion: MotionController { ...@@ -215,7 +215,7 @@ public class Motion: MotionController {
internal var toView: UIView? { internal var toView: UIView? {
return toViewController?.view return toViewController?.view
} }
/// An initializer. /// An initializer.
internal override init() { internal override init() {
super.init() super.init()
...@@ -236,7 +236,7 @@ public extension Motion { ...@@ -236,7 +236,7 @@ public extension Motion {
func setAnimationForNextTransition(_ animation: MotionTransitionType) { func setAnimationForNextTransition(_ animation: MotionTransitionType) {
defaultAnimation = animation defaultAnimation = animation
} }
/** /**
Set the container background color for the next transition. Set the container background color for the next transition.
- Parameter _ color: An optional UIColor. - Parameter _ color: An optional UIColor.
...@@ -305,7 +305,7 @@ internal extension Motion { ...@@ -305,7 +305,7 @@ internal extension Motion {
context.clean() context.clean()
if isFinished && isPresenting && toOverFullScreen { if isFinished && isPresenting && toOverFullScreen {
// finished presenting a overFullScreen VC // finished presenting a overFullScreen view controller.
context.unhide(rootView: tv) context.unhide(rootView: tv)
context.removeSnapshots(rootView: tv) context.removeSnapshots(rootView: tv)
context.storeViewAlpha(rootView: fv) context.storeViewAlpha(rootView: fv)
...@@ -314,7 +314,7 @@ internal extension Motion { ...@@ -314,7 +314,7 @@ internal extension Motion {
fv.removeFromSuperview() fv.removeFromSuperview()
fv.addSubview(c) fv.addSubview(c)
} else if !isFinished && !isPresenting && fromOverFullScreen { } else if !isFinished && !isPresenting && fromOverFullScreen {
// cancelled dismissing a overFullScreen VC // Cancelled dismissing a overFullScreen view controller.
context.unhide(rootView: fv) context.unhide(rootView: fv)
context.removeSnapshots(rootView: fv) context.removeSnapshots(rootView: fv)
context.storeViewAlpha(rootView: tv) context.storeViewAlpha(rootView: tv)
...@@ -328,7 +328,7 @@ internal extension Motion { ...@@ -328,7 +328,7 @@ internal extension Motion {
c.removeFromSuperview() c.removeFromSuperview()
} }
// move fromView & toView back from our container back to the one supplied by UIKit // Move fromView & toView back from our container back to the one supplied by UIKit.
if (toOverFullScreen && isFinished) || (fromOverFullScreen && !isFinished) { if (toOverFullScreen && isFinished) || (fromOverFullScreen && !isFinished) {
tc.addSubview(isFinished ? fv : tv) tc.addSubview(isFinished ? fv : tv)
} }
...@@ -336,7 +336,7 @@ internal extension Motion { ...@@ -336,7 +336,7 @@ internal extension Motion {
tc.addSubview(isFinished ? tv : fv) tc.addSubview(isFinished ? tv : fv)
if isPresenting != isFinished, !isContainerController { if isPresenting != isFinished, !isContainerController {
// only happens when present a .overFullScreen VC // Only happens when present a .overFullScreen view controller.
// bug: http://openradar.appspot.com/radar?id=5320103646199808 // bug: http://openradar.appspot.com/radar?id=5320103646199808
UIApplication.shared.keyWindow!.addSubview(isPresenting ? fv : tv) UIApplication.shared.keyWindow!.addSubview(isPresenting ? fv : tv)
} }
...@@ -418,8 +418,9 @@ fileprivate extension Motion { ...@@ -418,8 +418,9 @@ fileprivate extension Motion {
} }
context.loadViewAlpha(rootView: tv) context.loadViewAlpha(rootView: tv)
context.loadViewAlpha(rootView: fv)
v.addSubview(tv) v.addSubview(tv)
context.loadViewAlpha(rootView: fv)
v.addSubview(fv) v.addSubview(fv)
} }
...@@ -540,11 +541,11 @@ fileprivate extension Motion { ...@@ -540,11 +541,11 @@ fileprivate extension Motion {
guard let s = self else { guard let s = self else {
return return
} }
$0.motion?(motion: s, didEndTransitionTo: tvc) $0.motion?(motion: s, didEndTransitionTo: tvc)
$0.motionDidEndTransition?(motion: s) $0.motionDidEndTransition?(motion: s)
} }
processForMotionDelegate(viewController: tvc) { [weak self] in processForMotionDelegate(viewController: tvc) { [weak self] in
guard let s = self else { guard let s = self else {
return return
...@@ -620,7 +621,7 @@ internal extension Motion { ...@@ -620,7 +621,7 @@ internal extension Motion {
/** /**
A helper transition function. A helper transition function.
- Parameter from: A UIViewController. - Parameter from: A UIViewController.
- Parameter to: A UIViewController. - Parameter to: A UIViewController.
- Parameter in view: A UIView. - Parameter in view: A UIView.
- Parameter completion: An optional completion handler. - Parameter completion: An optional completion handler.
*/ */
...@@ -654,7 +655,7 @@ internal extension Motion { ...@@ -654,7 +655,7 @@ internal extension Motion {
let delegate = v.topViewController as? MotionViewControllerDelegate { let delegate = v.topViewController as? MotionViewControllerDelegate {
execute(delegate) execute(delegate)
} }
if let v = viewController as? UITabBarController, if let v = viewController as? UITabBarController,
let delegate = v.viewControllers?[v.selectedIndex] as? MotionViewControllerDelegate { let delegate = v.viewControllers?[v.selectedIndex] as? MotionViewControllerDelegate {
execute(delegate) execute(delegate)
......
...@@ -166,7 +166,7 @@ public extension MotionContext { ...@@ -166,7 +166,7 @@ public extension MotionContext {
if let v = viewToSnapshot[view] { if let v = viewToSnapshot[view] {
return v return v
} }
var containerView = container var containerView = container
let coordinateSpace = viewToTargetState[view]?.coordinateSpace ?? defaultCoordinateSpace let coordinateSpace = viewToTargetState[view]?.coordinateSpace ?? defaultCoordinateSpace
...@@ -311,7 +311,6 @@ public extension MotionContext { ...@@ -311,7 +311,6 @@ public extension MotionContext {
let nextSiblings = siblingViews[siblingViews.index(of: pairedView)!+1..<siblingViews.count] let nextSiblings = siblingViews[siblingViews.index(of: pairedView)!+1..<siblingViews.count]
containerView.addSubview(pairedSnapshot) containerView.addSubview(pairedSnapshot)
containerView.addSubview(snapshot)
for subview in pairedView.subviews { for subview in pairedView.subviews {
insertGlobalViewTree(view: subview) insertGlobalViewTree(view: subview)
...@@ -320,9 +319,6 @@ public extension MotionContext { ...@@ -320,9 +319,6 @@ public extension MotionContext {
for sibling in nextSiblings { for sibling in nextSiblings {
insertGlobalViewTree(view: sibling) insertGlobalViewTree(view: sibling)
} }
} else {
containerView.addSubview(snapshot)
} }
containerView.addSubview(snapshot) containerView.addSubview(snapshot)
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>FMWK</string> <string>FMWK</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2.8.1</string> <string>2.9.4</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
......
...@@ -31,17 +31,22 @@ ...@@ -31,17 +31,22 @@
import UIKit import UIKit
public struct Application { public struct Application {
/// A reference to the main UIWindow. /// An optional reference to the main UIWindow.
public static var keyWindow: UIWindow? { public static var keyWindow: UIWindow? {
return UIApplication.shared.keyWindow return UIApplication.shared.keyWindow
} }
/// A Boolean indicating if the device is in Landscape mode. /// An optional reference to the top most view controller.
public static var rootViewController: UIViewController? {
return keyWindow?.rootViewController
}
/// A boolean indicating if the device is in Landscape mode.
public static var isLandscape: Bool { public static var isLandscape: Bool {
return UIApplication.shared.statusBarOrientation.isLandscape return UIApplication.shared.statusBarOrientation.isLandscape
} }
/// A Boolean indicating if the device is in Portrait mode. /// A boolean indicating if the device is in Portrait mode.
public static var isPortrait: Bool { public static var isPortrait: Bool {
return !isLandscape return !isLandscape
} }
......
...@@ -265,6 +265,7 @@ open class Bar: View { ...@@ -265,6 +265,7 @@ open class Bar: View {
autoresizingMask = .flexibleWidth autoresizingMask = .flexibleWidth
interimSpacePreset = .interimSpace3 interimSpacePreset = .interimSpace3
contentEdgeInsetsPreset = .square1 contentEdgeInsetsPreset = .square1
prepareContentView() prepareContentView()
} }
} }
......
...@@ -130,19 +130,20 @@ open class BottomNavigationController: UITabBarController { ...@@ -130,19 +130,20 @@ open class BottomNavigationController: UITabBarController {
when subclassing. when subclassing.
*/ */
open func prepare() { open func prepare() {
view.backgroundColor = .white
view.clipsToBounds = true view.clipsToBounds = true
view.contentScaleFactor = Screen.scale view.backgroundColor = .white
view.contentScaleFactor = Screen.scale
prepareTabBar() prepareTabBar()
} }
} }
extension BottomNavigationController { fileprivate extension BottomNavigationController {
/// Prepares the tabBar. /// Prepares the tabBar.
fileprivate func prepareTabBar() { func prepareTabBar() {
tabBar.isTranslucent = false tabBar.isTranslucent = false
tabBar.heightPreset = .normal tabBar.heightPreset = .normal
tabBar.depthPreset = .depth1 tabBar.dividerColor = Color.grey.lighten3
tabBar.dividerAlignment = .top tabBar.dividerAlignment = .top
let image = UIImage() let image = UIImage()
......
...@@ -228,7 +228,7 @@ open class Card: PulseView { ...@@ -228,7 +228,7 @@ open class Card: PulseView {
if 0 == h || nil != view as? UILabel { if 0 == h || nil != view as? UILabel {
(view as? UILabel)?.sizeToFit() (view as? UILabel)?.sizeToFit()
h = view.sizeThatFits(CGSize(width: w, height: CGFloat.greatestFiniteMagnitude)).height h = view.sizeThatFits(CGSize(width: w, height: .greatestFiniteMagnitude)).height
} }
view.width = w view.width = w
......
...@@ -108,7 +108,7 @@ extension NSMutableAttributedString { ...@@ -108,7 +108,7 @@ extension NSMutableAttributedString {
- Parameter value: Any type. - Parameter value: Any type.
- Parameter range: A NSRange. - Parameter range: A NSRange.
*/ */
open func addAttribute(characterAttribute: CharacterAttribute, value: Any, range: NSRange) { open func add(characterAttribute: CharacterAttribute, value: Any, range: NSRange) {
addAttribute(CharacterAttributeToValue(attribute: characterAttribute), value: value, range: range) addAttribute(CharacterAttributeToValue(attribute: characterAttribute), value: value, range: range)
} }
...@@ -117,9 +117,9 @@ extension NSMutableAttributedString { ...@@ -117,9 +117,9 @@ extension NSMutableAttributedString {
- Parameter characterAttributes: A Dictionary of CharacterAttribute type keys and Any type values. - Parameter characterAttributes: A Dictionary of CharacterAttribute type keys and Any type values.
- Parameter range: A NSRange. - Parameter range: A NSRange.
*/ */
open func addAttributes(characterAttributes: [CharacterAttribute: Any], range: NSRange) { open func add(characterAttributes: [CharacterAttribute: Any], range: NSRange) {
for (k, v) in characterAttributes { for (k, v) in characterAttributes {
addAttribute(characterAttribute: k, value: v, range: range) add(characterAttribute: k, value: v, range: range)
} }
} }
...@@ -129,9 +129,9 @@ extension NSMutableAttributedString { ...@@ -129,9 +129,9 @@ extension NSMutableAttributedString {
- Parameter value: Any type. - Parameter value: Any type.
- Parameter range: A NSRange. - Parameter range: A NSRange.
*/ */
open func updateAttribute(characterAttribute: CharacterAttribute, value: Any, range: NSRange) { open func update(characterAttribute: CharacterAttribute, value: Any, range: NSRange) {
removeAttribute(characterAttribute: characterAttribute, range: range) remove(characterAttribute: characterAttribute, range: range)
addAttribute(characterAttribute: characterAttribute, value: value, range: range) add(characterAttribute: characterAttribute, value: value, range: range)
} }
/** /**
...@@ -139,9 +139,9 @@ extension NSMutableAttributedString { ...@@ -139,9 +139,9 @@ extension NSMutableAttributedString {
- Parameter characterAttributes: A Dictionary of CharacterAttribute type keys and Any type values. - Parameter characterAttributes: A Dictionary of CharacterAttribute type keys and Any type values.
- Parameter range: A NSRange. - Parameter range: A NSRange.
*/ */
open func updateAttributes(characterAttributes: [CharacterAttribute: Any], range: NSRange) { open func update(characterAttributes: [CharacterAttribute: Any], range: NSRange) {
for (k, v) in characterAttributes { for (k, v) in characterAttributes {
updateAttribute(characterAttribute: k, value: v, range: range) update(characterAttribute: k, value: v, range: range)
} }
} }
...@@ -150,7 +150,7 @@ extension NSMutableAttributedString { ...@@ -150,7 +150,7 @@ extension NSMutableAttributedString {
- Parameter characterAttribute: A CharacterAttribute. - Parameter characterAttribute: A CharacterAttribute.
- Parameter range: A NSRange. - Parameter range: A NSRange.
*/ */
open func removeAttribute(characterAttribute: CharacterAttribute, range: NSRange) { open func remove(characterAttribute: CharacterAttribute, range: NSRange) {
removeAttribute(CharacterAttributeToValue(attribute: characterAttribute), range: range) removeAttribute(CharacterAttributeToValue(attribute: characterAttribute), range: range)
} }
...@@ -159,9 +159,9 @@ extension NSMutableAttributedString { ...@@ -159,9 +159,9 @@ extension NSMutableAttributedString {
- Parameter characterAttributes: An Array of CharacterAttributes. - Parameter characterAttributes: An Array of CharacterAttributes.
- Parameter range: A NSRange. - Parameter range: A NSRange.
*/ */
open func removeAttributes(characterAttributes: [CharacterAttribute], range: NSRange) { open func remove(characterAttributes: [CharacterAttribute], range: NSRange) {
for k in characterAttributes { for k in characterAttributes {
removeAttribute(characterAttribute: k, range: range) remove(characterAttribute: k, range: range)
} }
} }
} }
/*
* Copyright (C) 2015 - 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import UIKit
fileprivate var ChipItemKey: UInt8 = 0
@objc(ChipBarAlignment)
public enum ChipBarAlignment: Int {
case top
case bottom
case hidden
}
extension UIViewController {
/**
A convenience property that provides access to the ChipBarController.
This is the recommended method of accessing the ChipBarController
through child UIViewControllers.
*/
public var chipsController: ChipBarController? {
return traverseViewControllerHierarchyForClassType()
}
}
open class ChipBarController: TransitionController {
/**
A Display value to indicate whether or not to
display the rootViewController to the full view
bounds, or up to the toolbar height.
*/
open var displayStyle = DisplayStyle.partial {
didSet {
layoutSubviews()
}
}
/// The ChipBar used to switch between view controllers.
@IBInspectable
open let chipBar = ChipBar()
/// The chipBar alignment.
open var chipBarAlignment = ChipBarAlignment.bottom {
didSet {
layoutSubviews()
}
}
open override func layoutSubviews() {
super.layoutSubviews()
layoutChipBar()
layoutContainer()
layoutRootViewController()
}
open override func prepare() {
super.prepare()
prepareChipBar()
}
}
fileprivate extension ChipBarController {
/// Prepares the ChipBar.
func prepareChipBar() {
chipBar.depthPreset = .depth1
view.addSubview(chipBar)
}
}
fileprivate extension ChipBarController {
/// Layout the container.
func layoutContainer() {
chipBar.width = view.width
switch displayStyle {
case .partial:
let p = chipBar.height
let y = view.height - p
switch chipBarAlignment {
case .top:
container.y = p
container.height = y
case .bottom:
container.y = 0
container.height = y
case .hidden:
container.y = 0
container.height = view.height
}
container.width = view.width
case .full:
container.frame = view.bounds
}
}
/// Layout the chipBar.
func layoutChipBar() {
chipBar.width = view.width
switch chipBarAlignment {
case .top:
chipBar.isHidden = false
chipBar.y = 0
case .bottom:
chipBar.isHidden = false
chipBar.y = view.height - chipBar.height
case .hidden:
chipBar.isHidden = true
}
}
/// Layout the rootViewController.
func layoutRootViewController() {
rootViewController.view.frame = container.bounds
}
}
...@@ -31,13 +31,6 @@ ...@@ -31,13 +31,6 @@
import UIKit import UIKit
open class FABButton: Button { open class FABButton: Button {
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open override func prepare() { open override func prepare() {
super.prepare() super.prepare()
depthPreset = .depth1 depthPreset = .depth1
......
...@@ -43,18 +43,11 @@ extension UIViewController { ...@@ -43,18 +43,11 @@ extension UIViewController {
through child UIViewControllers. through child UIViewControllers.
*/ */
public var fabMenuController: FABMenuController? { public var fabMenuController: FABMenuController? {
var viewController: UIViewController? = self return traverseViewControllerHierarchyForClassType()
while nil != viewController {
if viewController is FABMenuController {
return viewController as? FABMenuController
}
viewController = viewController?.parent
}
return nil
} }
} }
open class FABMenuController: RootController { open class FABMenuController: TransitionController {
/// Reference to the MenuView. /// Reference to the MenuView.
@IBInspectable @IBInspectable
open let fabMenu = FABMenu() open let fabMenu = FABMenu()
...@@ -86,29 +79,42 @@ extension FABMenuController { ...@@ -86,29 +79,42 @@ extension FABMenuController {
fileprivate func prepareFABMenu() { fileprivate func prepareFABMenu() {
fabMenu.delegate = self fabMenu.delegate = self
fabMenu.zPosition = 1000 fabMenu.zPosition = 1000
fabMenu.handleFABButtonCallback = handleFABButtonCallback
fabMenu.handleOpenCallback = handleOpenCallback fabMenu.handleFABButtonCallback = { [weak self] in
fabMenu.handleCloseCallback = handleCloseCallback self?.handleFABButtonCallback(button: $0)
fabMenu.handleCompletionCallback = handleCompletionCallback }
fabMenu.handleOpenCallback = { [weak self] in
self?.handleOpenCallback()
}
fabMenu.handleCloseCallback = { [weak self] in
self?.handleCloseCallback()
}
fabMenu.handleCompletionCallback = { [weak self] in
self?.handleCompletionCallback(view: $0)
}
view.addSubview(fabMenu) view.addSubview(fabMenu)
} }
} }
extension FABMenuController { fileprivate extension FABMenuController {
/// Shows the fabMenuBacking. /// Shows the fabMenuBacking.
fileprivate func showFabMenuBacking() { func showFabMenuBacking() {
showFade() showFade()
showBlurView() showBlurView()
} }
/// Hides the fabMenuBacking. /// Hides the fabMenuBacking.
fileprivate func hideFabMenuBacking() { func hideFabMenuBacking() {
hideFade() hideFade()
hideBlurView() hideBlurView()
} }
/// Shows the blurView. /// Shows the blurView.
fileprivate func showBlurView() { func showBlurView() {
guard .blur == fabMenuBacking else { guard .blur == fabMenuBacking else {
return return
} }
...@@ -129,7 +135,7 @@ extension FABMenuController { ...@@ -129,7 +135,7 @@ extension FABMenuController {
} }
/// Hides the blurView. /// Hides the blurView.
fileprivate func hideBlurView() { func hideBlurView() {
guard .blur == fabMenuBacking else { guard .blur == fabMenuBacking else {
return return
} }
...@@ -143,7 +149,7 @@ extension FABMenuController { ...@@ -143,7 +149,7 @@ extension FABMenuController {
} }
/// Shows the fade. /// Shows the fade.
fileprivate func showFade() { func showFade() {
guard .fade == fabMenuBacking else { guard .fade == fabMenuBacking else {
return return
} }
...@@ -158,7 +164,7 @@ extension FABMenuController { ...@@ -158,7 +164,7 @@ extension FABMenuController {
} }
/// Hides the fade. /// Hides the fade.
fileprivate func hideFade() { func hideFade() {
guard .fade == fabMenuBacking else { guard .fade == fabMenuBacking else {
return return
} }
...@@ -173,13 +179,12 @@ extension FABMenuController { ...@@ -173,13 +179,12 @@ extension FABMenuController {
} }
} }
extension FABMenuController { fileprivate extension FABMenuController {
/** /**
Handler to toggle the FABMenu opened or closed. Handler to toggle the FABMenu opened or closed.
- Parameter button: A UIButton. - Parameter button: A UIButton.
*/ */
@objc func handleFABButtonCallback(button: UIButton) {
fileprivate func handleFABButtonCallback(button: UIButton) {
guard fabMenu.isOpened else { guard fabMenu.isOpened else {
fabMenu.open(isTriggeredByUserInteraction: true) fabMenu.open(isTriggeredByUserInteraction: true)
return return
...@@ -189,15 +194,13 @@ extension FABMenuController { ...@@ -189,15 +194,13 @@ extension FABMenuController {
} }
/// Handler for when the FABMenu.open function is called. /// Handler for when the FABMenu.open function is called.
@objc func handleOpenCallback() {
fileprivate func handleOpenCallback() {
isUserInteractionEnabled = false isUserInteractionEnabled = false
showFabMenuBacking() showFabMenuBacking()
} }
/// Handler for when the FABMenu.close function is called. /// Handler for when the FABMenu.close function is called.
@objc func handleCloseCallback() {
fileprivate func handleCloseCallback() {
isUserInteractionEnabled = false isUserInteractionEnabled = false
hideFabMenuBacking() hideFabMenuBacking()
} }
...@@ -206,7 +209,7 @@ extension FABMenuController { ...@@ -206,7 +209,7 @@ extension FABMenuController {
Completion handler for FABMenu open and close calls. Completion handler for FABMenu open and close calls.
- Parameter view: A UIView. - Parameter view: A UIView.
*/ */
fileprivate func handleCompletionCallback(view: UIView) { func handleCompletionCallback(view: UIView) {
if view == fabMenu.fabMenuItems.last { if view == fabMenu.fabMenuItems.last {
isUserInteractionEnabled = true isUserInteractionEnabled = true
} }
......
...@@ -31,13 +31,6 @@ ...@@ -31,13 +31,6 @@
import UIKit import UIKit
open class FlatButton: Button { open class FlatButton: Button {
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open override func prepare() { open override func prepare() {
super.prepare() super.prepare()
cornerRadiusPreset = .cornerRadius1 cornerRadiusPreset = .cornerRadius1
......
...@@ -31,14 +31,7 @@ ...@@ -31,14 +31,7 @@
import UIKit import UIKit
open class IconButton: Button { open class IconButton: Button {
/** open override func prepare() {
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open override func prepare() {
super.prepare() super.prepare()
pulseAnimation = .center pulseAnimation = .center
} }
......
...@@ -30,12 +30,6 @@ ...@@ -30,12 +30,6 @@
import UIKit import UIKit
@objc(ToolbarAlignment)
public enum ToolbarAlignment: Int {
case top
case bottom
}
open class ImageCard: Card { open class ImageCard: Card {
/** /**
A Display value to indicate whether or not to A Display value to indicate whether or not to
......
...@@ -38,19 +38,12 @@ public enum NavigationDrawerPosition: Int { ...@@ -38,19 +38,12 @@ public enum NavigationDrawerPosition: Int {
extension UIViewController { extension UIViewController {
/** /**
A convenience property that provides access to the NavigationDrawerController. A convenience property that provides access to the NavigationDrawerController.
This is the recommended method of accessing the NavigationDrawerController This is the recommended method of accessing the NavigationDrawerController
through child UIViewControllers. through child UIViewControllers.
*/ */
public var navigationDrawerController: NavigationDrawerController? { public var navigationDrawerController: NavigationDrawerController? {
var viewController: UIViewController? = self return traverseViewControllerHierarchyForClassType()
while nil != viewController {
if viewController is NavigationDrawerController {
return viewController as? NavigationDrawerController
}
viewController = viewController?.parent
}
return nil
} }
} }
...@@ -143,7 +136,7 @@ public protocol NavigationDrawerControllerDelegate { ...@@ -143,7 +136,7 @@ public protocol NavigationDrawerControllerDelegate {
} }
@objc(NavigationDrawerController) @objc(NavigationDrawerController)
open class NavigationDrawerController: RootController { open class NavigationDrawerController: TransitionController {
/** /**
A CGFloat property that is used internally to track A CGFloat property that is used internally to track
the original (x) position of the container view when panning. the original (x) position of the container view when panning.
...@@ -415,8 +408,8 @@ open class NavigationDrawerController: RootController { ...@@ -415,8 +408,8 @@ open class NavigationDrawerController: RootController {
prepare() prepare()
} }
open override func transition(to viewController: UIViewController, duration: TimeInterval = 0.5, options: UIViewAnimationOptions = [], animations: (() -> Void)? = nil, completion: ((Bool) -> Void)? = nil) { open override func transition(to viewController: UIViewController, completion: ((Bool) -> Void)? = nil) {
super.transition(to: viewController, duration: duration, options: options, animations: animations) { [weak self, completion = completion] (result) in super.transition(to: viewController) { [weak self, completion = completion] (result) in
guard let s = self else { guard let s = self else {
return return
} }
...@@ -452,6 +445,8 @@ open class NavigationDrawerController: RootController { ...@@ -452,6 +445,8 @@ open class NavigationDrawerController: RootController {
vc.view.center = CGPoint(x: rightViewWidth / 2, y: v.bounds.height / 2) vc.view.center = CGPoint(x: rightViewWidth / 2, y: v.bounds.height / 2)
} }
} }
rootViewController.view.frame = container.bounds
} }
open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
...@@ -976,7 +971,7 @@ extension NavigationDrawerController { ...@@ -976,7 +971,7 @@ extension NavigationDrawerController {
/// Prepares the contentViewController. /// Prepares the contentViewController.
fileprivate func prepareContentViewController() { fileprivate func prepareContentViewController() {
contentViewController.view.backgroundColor = .black contentViewController.view.backgroundColor = .black
prepare(viewController: contentViewController, withContainer: view) prepare(viewController: contentViewController, in: view)
view.sendSubview(toBack: contentViewController.view) view.sendSubview(toBack: contentViewController.view)
} }
...@@ -986,7 +981,7 @@ extension NavigationDrawerController { ...@@ -986,7 +981,7 @@ extension NavigationDrawerController {
return return
} }
prepare(viewController: leftViewController, withContainer: v) prepare(viewController: leftViewController, in: v)
} }
/// A method that prepares the rightViewController. /// A method that prepares the rightViewController.
...@@ -995,7 +990,7 @@ extension NavigationDrawerController { ...@@ -995,7 +990,7 @@ extension NavigationDrawerController {
return return
} }
prepare(viewController: rightViewController, withContainer: v) prepare(viewController: rightViewController, in: v)
} }
/// A method that prepares the leftView. /// A method that prepares the leftView.
...@@ -1046,6 +1041,7 @@ extension NavigationDrawerController { ...@@ -1046,6 +1041,7 @@ extension NavigationDrawerController {
leftPanGesture = UIPanGestureRecognizer(target: self, action: #selector(handleLeftViewPanGesture(recognizer:))) leftPanGesture = UIPanGestureRecognizer(target: self, action: #selector(handleLeftViewPanGesture(recognizer:)))
leftPanGesture!.delegate = self leftPanGesture!.delegate = self
leftPanGesture!.cancelsTouchesInView = false
view.addGestureRecognizer(leftPanGesture!) view.addGestureRecognizer(leftPanGesture!)
} }
...@@ -1069,6 +1065,7 @@ extension NavigationDrawerController { ...@@ -1069,6 +1065,7 @@ extension NavigationDrawerController {
rightPanGesture = UIPanGestureRecognizer(target: self, action: #selector(handleRightViewPanGesture(recognizer:))) rightPanGesture = UIPanGestureRecognizer(target: self, action: #selector(handleRightViewPanGesture(recognizer:)))
rightPanGesture!.delegate = self rightPanGesture!.delegate = self
rightPanGesture!.cancelsTouchesInView = false
view.addGestureRecognizer(rightPanGesture!) view.addGestureRecognizer(rightPanGesture!)
} }
......
...@@ -84,8 +84,16 @@ public class NavigationItem: NSObject { ...@@ -84,8 +84,16 @@ public class NavigationItem: NSObject {
} }
} }
/// An optional reference to the NavigationBar.
public var navigationBar: NavigationBar? { public var navigationBar: NavigationBar? {
return contentView.superview?.superview as? NavigationBar var v = contentView.superview
while nil != v {
if let navigationBar = v as? NavigationBar {
return navigationBar
}
v = v?.superview
}
return nil
} }
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
......
...@@ -31,13 +31,6 @@ ...@@ -31,13 +31,6 @@
import UIKit import UIKit
open class RaisedButton: Button { open class RaisedButton: Button {
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open override func prepare() { open override func prepare() {
super.prepare() super.prepare()
depthPreset = .depth1 depthPreset = .depth1
......
...@@ -148,18 +148,11 @@ open class SearchBar: Bar { ...@@ -148,18 +148,11 @@ open class SearchBar: Bar {
return return
} }
textField.frame = contentView.bounds layoutTextField()
layoutLeftView() layoutLeftView()
layoutClearButton() layoutClearButton()
} }
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open override func prepare() { open override func prepare() {
super.prepare() super.prepare()
prepareTextField() prepareTextField()
...@@ -168,10 +161,9 @@ open class SearchBar: Bar { ...@@ -168,10 +161,9 @@ open class SearchBar: Bar {
} }
extension SearchBar { extension SearchBar {
/// Layout the clearButton. /// Layout the textField.
open func layoutClearButton() { open func layoutTextField() {
let h = textField.frame.height textField.frame = contentView.bounds
clearButton.frame = CGRect(x: textField.frame.width - h - 4, y: 4, width: h, height: h - 8)
} }
/// Layout the leftView. /// Layout the leftView.
...@@ -185,12 +177,18 @@ extension SearchBar { ...@@ -185,12 +177,18 @@ extension SearchBar {
(v as? UIImageView)?.contentMode = .scaleAspectFit (v as? UIImageView)?.contentMode = .scaleAspectFit
} }
/// Layout the clearButton.
open func layoutClearButton() {
let h = textField.frame.height
clearButton.frame = CGRect(x: textField.frame.width - h - 4, y: 4, width: h, height: h - 8)
}
} }
extension SearchBar { fileprivate extension SearchBar {
/// Clears the textField text. /// Clears the textField text.
@objc @objc
fileprivate func handleClearButton() { func handleClearButton() {
guard nil == textField.delegate?.textFieldShouldClear || true == textField.delegate?.textFieldShouldClear?(textField) else { guard nil == textField.delegate?.textFieldShouldClear || true == textField.delegate?.textFieldShouldClear?(textField) else {
return return
} }
...@@ -206,14 +204,14 @@ extension SearchBar { ...@@ -206,14 +204,14 @@ extension SearchBar {
// Live updates the search results. // Live updates the search results.
@objc @objc
fileprivate func handleEditingChanged(textField: UITextField) { func handleEditingChanged(textField: UITextField) {
delegate?.searchBar?(searchBar: self, didChange: textField, with: textField.text) delegate?.searchBar?(searchBar: self, didChange: textField, with: textField.text)
} }
} }
extension SearchBar { fileprivate extension SearchBar {
/// Prepares the textField. /// Prepares the textField.
fileprivate func prepareTextField() { func prepareTextField() {
textField.contentScaleFactor = Screen.scale textField.contentScaleFactor = Screen.scale
textField.font = RobotoFont.regular(with: 17) textField.font = RobotoFont.regular(with: 17)
textField.backgroundColor = Color.clear textField.backgroundColor = Color.clear
...@@ -226,7 +224,7 @@ extension SearchBar { ...@@ -226,7 +224,7 @@ extension SearchBar {
} }
/// Prepares the clearButton. /// Prepares the clearButton.
fileprivate func prepareClearButton() { func prepareClearButton() {
clearButton = IconButton(image: Icon.cm.close, tintColor: placeholderColor) clearButton = IconButton(image: Icon.cm.close, tintColor: placeholderColor)
clearButton.contentEdgeInsets = .zero clearButton.contentEdgeInsets = .zero
isClearButtonAutoHandleEnabled = true isClearButtonAutoHandleEnabled = true
......
...@@ -30,22 +30,21 @@ ...@@ -30,22 +30,21 @@
import UIKit import UIKit
extension UIViewController { @objc(SearchBarAlignment)
/** public enum SearchBarAlignment: Int {
case top
case bottom
}
public extension UIViewController {
/**
A convenience property that provides access to the SearchBarController. A convenience property that provides access to the SearchBarController.
This is the recommended method of accessing the SearchBarController This is the recommended method of accessing the SearchBarController
through child UIViewControllers. through child UIViewControllers.
*/ */
public var searchBarController: SearchBarController? { var searchBarController: SearchBarController? {
var viewController: UIViewController? = self return traverseViewControllerHierarchyForClassType()
while nil != viewController { }
if viewController is SearchBarController {
return viewController as? SearchBarController
}
viewController = viewController?.parent
}
return nil
}
} }
open class SearchBarController: StatusBarController { open class SearchBarController: StatusBarController {
...@@ -53,49 +52,71 @@ open class SearchBarController: StatusBarController { ...@@ -53,49 +52,71 @@ open class SearchBarController: StatusBarController {
@IBInspectable @IBInspectable
open let searchBar = SearchBar() open let searchBar = SearchBar()
/// The searchBar alignment.
open var searchBarAlignment = SearchBarAlignment.top {
didSet {
layoutSubviews()
}
}
open override func layoutSubviews() { open override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
layoutSearchBar()
let y = Application.shouldStatusBarBeHidden || statusBar.isHidden ? 0 : statusBar.height layoutContainer()
layoutRootViewController()
searchBar.y = y
searchBar.width = view.width
switch displayStyle {
case .partial:
let h = y + searchBar.height
rootViewController.view.y = h
rootViewController.view.height = view.height - h
case .full:
rootViewController.view.frame = view.bounds
}
} }
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open override func prepare() { open override func prepare() {
super.prepare() super.prepare()
displayStyle = .partial displayStyle = .partial
prepareStatusBar()
prepareSearchBar() prepareSearchBar()
} }
} }
extension SearchBarController { fileprivate extension SearchBarController {
/// Prepares the statusBar.
fileprivate func prepareStatusBar() {
shouldHideStatusBarOnRotation = false
}
/// Prepares the searchBar. /// Prepares the searchBar.
fileprivate func prepareSearchBar() { func prepareSearchBar() {
searchBar.depthPreset = .depth1
searchBar.zPosition = 1000 searchBar.zPosition = 1000
searchBar.depthPreset = .depth1
view.addSubview(searchBar) view.addSubview(searchBar)
} }
} }
fileprivate extension SearchBarController {
/// Layout the container.
func layoutContainer() {
switch displayStyle {
case .partial:
let p = searchBar.height
let q = statusBarOffsetAdjustment
let h = view.height - p - q
switch searchBarAlignment {
case .top:
container.y = q + p
container.height = h
case .bottom:
container.y = q
container.height = h
}
container.width = view.width
case .full:
container.frame = view.bounds
}
}
/// Layout the searchBar.
func layoutSearchBar() {
searchBar.x = 0
searchBar.y = .top == searchBarAlignment ? statusBarOffsetAdjustment : view.height - searchBar.height
searchBar.width = view.width
}
/// Layout the rootViewController.
func layoutRootViewController() {
rootViewController.view.frame = container.bounds
}
}
...@@ -78,18 +78,11 @@ extension UIViewController { ...@@ -78,18 +78,11 @@ extension UIViewController {
through child UIViewControllers. through child UIViewControllers.
*/ */
public var snackbarController: SnackbarController? { public var snackbarController: SnackbarController? {
var viewController: UIViewController? = self return traverseViewControllerHierarchyForClassType()
while nil != viewController {
if viewController is SnackbarController {
return viewController as? SnackbarController
}
viewController = viewController?.parent
}
return nil
} }
} }
open class SnackbarController: RootController { open class SnackbarController: TransitionController {
/// Reference to the Snackbar. /// Reference to the Snackbar.
open let snackbar = Snackbar() open let snackbar = Snackbar()
......
...@@ -37,18 +37,11 @@ extension UIViewController { ...@@ -37,18 +37,11 @@ extension UIViewController {
through child UIViewControllers. through child UIViewControllers.
*/ */
public var statusBarController: StatusBarController? { public var statusBarController: StatusBarController? {
var viewController: UIViewController? = self return traverseViewControllerHierarchyForClassType()
while nil != viewController {
if viewController is StatusBarController {
return viewController as? StatusBarController
}
viewController = viewController?.parent
}
return nil
} }
} }
open class StatusBarController: RootController { open class StatusBarController: TransitionController {
/** /**
A Display value to indicate whether or not to A Display value to indicate whether or not to
display the rootViewController to the full view display the rootViewController to the full view
...@@ -81,19 +74,20 @@ open class StatusBarController: RootController { ...@@ -81,19 +74,20 @@ open class StatusBarController: RootController {
} }
} }
/// An adjustment based on the rules for displaying the statusBar.
open var statusBarOffsetAdjustment: CGFloat {
return Application.shouldStatusBarBeHidden || statusBar.isHidden ? 0 : statusBar.height
}
/// A boolean that indicates to hide the statusBar on rotation. /// A boolean that indicates to hide the statusBar on rotation.
open var shouldHideStatusBarOnRotation = true open var shouldHideStatusBarOnRotation = false
/// A reference to the statusBar. /// A reference to the statusBar.
open let statusBar = UIView() open let statusBar = UIView()
/**
To execute in the order of the layout chain, override this
method. LayoutSubviews should be called immediately, unless you
have a certain need.
*/
open override func layoutSubviews() { open override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
if shouldHideStatusBarOnRotation { if shouldHideStatusBarOnRotation {
statusBar.isHidden = Application.shouldStatusBarBeHidden statusBar.isHidden = Application.shouldStatusBarBeHidden
} }
...@@ -103,20 +97,17 @@ open class StatusBarController: RootController { ...@@ -103,20 +97,17 @@ open class StatusBarController: RootController {
switch displayStyle { switch displayStyle {
case .partial: case .partial:
let h = statusBar.height let h = statusBar.height
rootViewController.view.y = h container.y = h
rootViewController.view.height = view.height - h container.height = view.height - h
case .full: case .full:
rootViewController.view.frame = view.bounds container.frame = view.bounds
} }
}
rootViewController.view.frame = container.bounds
container.zPosition = statusBar.zPosition + (Application.shouldStatusBarBeHidden ? 1 : -1)
}
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open override func prepare() { open override func prepare() {
super.prepare() super.prepare()
prepareStatusBar() prepareStatusBar()
......
...@@ -40,27 +40,27 @@ public enum TextFieldPlaceholderAnimation: Int { ...@@ -40,27 +40,27 @@ public enum TextFieldPlaceholderAnimation: Int {
public protocol TextFieldDelegate: UITextFieldDelegate { public protocol TextFieldDelegate: UITextFieldDelegate {
/** /**
A delegation method that is executed when the textField changed. A delegation method that is executed when the textField changed.
- Parameter textField: A UITextField. - Parameter textField: A TextField.
- Parameter didChange text: An optional String. - Parameter didChange text: An optional String.
*/ */
@objc @objc
optional func textField(textField: UITextField, didChange text: String?) optional func textField(textField: TextField, didChange text: String?)
/** /**
A delegation method that is executed when the textField will clear. A delegation method that is executed when the textField will clear.
- Parameter textField: A UITextField. - Parameter textField: A TextField.
- Parameter willClear text: An optional String. - Parameter willClear text: An optional String.
*/ */
@objc @objc
optional func textField(textField: UITextField, willClear text: String?) optional func textField(textField: TextField, willClear text: String?)
/** /**
A delegation method that is executed when the textField is cleared. A delegation method that is executed when the textField is cleared.
- Parameter textField: A UITextField. - Parameter textField: A TextField.
- Parameter didClear text: An optional String. - Parameter didClear text: An optional String.
*/ */
@objc @objc
optional func textField(textField: UITextField, didClear text: String?) optional func textField(textField: TextField, didClear text: String?)
} }
open class TextField: UITextField { open class TextField: UITextField {
...@@ -283,7 +283,7 @@ open class TextField: UITextField { ...@@ -283,7 +283,7 @@ open class TextField: UITextField {
clearButtonMode = .never clearButtonMode = .never
rightViewMode = .whileEditing rightViewMode = .whileEditing
rightView = clearIconButton rightView = clearIconButton
isClearIconButtonAutoHandled = isClearIconButtonAutoHandled ? true : false isClearIconButtonAutoHandled = { isClearIconButtonAutoHandled }()
layoutSubviews() layoutSubviews()
} }
...@@ -330,7 +330,7 @@ open class TextField: UITextField { ...@@ -330,7 +330,7 @@ open class TextField: UITextField {
clearButtonMode = .never clearButtonMode = .never
rightViewMode = .whileEditing rightViewMode = .whileEditing
rightView = visibilityIconButton rightView = visibilityIconButton
isVisibilityIconButtonAutoHandled = isVisibilityIconButtonAutoHandled ? true : false isVisibilityIconButtonAutoHandled = { isVisibilityIconButtonAutoHandled }()
layoutSubviews() layoutSubviews()
} }
...@@ -338,7 +338,7 @@ open class TextField: UITextField { ...@@ -338,7 +338,7 @@ open class TextField: UITextField {
/// Enables the automatic handling of the visibilityIconButton. /// Enables the automatic handling of the visibilityIconButton.
@IBInspectable @IBInspectable
open var isVisibilityIconButtonAutoHandled: Bool = true { open var isVisibilityIconButtonAutoHandled = true {
didSet { didSet {
visibilityIconButton?.removeTarget(self, action: #selector(handleVisibilityIconButton), for: .touchUpInside) visibilityIconButton?.removeTarget(self, action: #selector(handleVisibilityIconButton), for: .touchUpInside)
...@@ -428,21 +428,21 @@ open class TextField: UITextField { ...@@ -428,21 +428,21 @@ open class TextField: UITextField {
} }
} }
extension TextField { fileprivate extension TextField {
/// Prepares the divider. /// Prepares the divider.
fileprivate func prepareDivider() { func prepareDivider() {
dividerColor = dividerNormalColor dividerColor = dividerNormalColor
} }
/// Prepares the placeholderLabel. /// Prepares the placeholderLabel.
fileprivate func preparePlaceholderLabel() { func preparePlaceholderLabel() {
placeholderNormalColor = Color.darkText.others placeholderNormalColor = Color.darkText.others
placeholderLabel.backgroundColor = .clear placeholderLabel.backgroundColor = .clear
addSubview(placeholderLabel) addSubview(placeholderLabel)
} }
/// Prepares the detailLabel. /// Prepares the detailLabel.
fileprivate func prepareDetailLabel() { func prepareDetailLabel() {
detailLabel.font = RobotoFont.regular(with: 12) detailLabel.font = RobotoFont.regular(with: 12)
detailLabel.numberOfLines = 0 detailLabel.numberOfLines = 0
detailColor = Color.darkText.others detailColor = Color.darkText.others
...@@ -450,46 +450,46 @@ extension TextField { ...@@ -450,46 +450,46 @@ extension TextField {
} }
/// Prepares the leftView. /// Prepares the leftView.
fileprivate func prepareLeftView() { func prepareLeftView() {
leftView?.contentMode = .left leftView?.contentMode = .left
leftViewMode = .always leftViewMode = .always
updateLeftViewColor() updateLeftViewColor()
} }
/// Prepares the target handlers. /// Prepares the target handlers.
fileprivate func prepareTargetHandlers() { func prepareTargetHandlers() {
addTarget(self, action: #selector(handleEditingDidBegin), for: .editingDidBegin) addTarget(self, action: #selector(handleEditingDidBegin), for: .editingDidBegin)
addTarget(self, action: #selector(handleEditingChanged), for: .editingChanged) addTarget(self, action: #selector(handleEditingChanged), for: .editingChanged)
addTarget(self, action: #selector(handleEditingDidEnd), for: .editingDidEnd) addTarget(self, action: #selector(handleEditingDidEnd), for: .editingDidEnd)
} }
/// Prepares the textAlignment. /// Prepares the textAlignment.
fileprivate func prepareTextAlignment() { func prepareTextAlignment() {
textAlignment = .rightToLeft == Application.userInterfaceLayoutDirection ? .right : .left textAlignment = .rightToLeft == Application.userInterfaceLayoutDirection ? .right : .left
} }
} }
extension TextField { fileprivate extension TextField {
/// Updates the leftView tint color. /// Updates the leftView tint color.
fileprivate func updateLeftViewColor() { func updateLeftViewColor() {
leftView?.tintColor = isEditing ? leftViewActiveColor : leftViewNormalColor leftView?.tintColor = isEditing ? leftViewActiveColor : leftViewNormalColor
} }
/// Updates the placeholderLabel text color. /// Updates the placeholderLabel text color.
fileprivate func updatePlaceholderLabelColor() { func updatePlaceholderLabelColor() {
tintColor = placeholderActiveColor tintColor = placeholderActiveColor
placeholderLabel.textColor = isEditing ? placeholderActiveColor : placeholderNormalColor placeholderLabel.textColor = isEditing ? placeholderActiveColor : placeholderNormalColor
} }
/// Updates the detailLabel text color. /// Updates the detailLabel text color.
fileprivate func updateDetailLabelColor() { func updateDetailLabelColor() {
detailLabel.textColor = detailColor detailLabel.textColor = detailColor
} }
} }
extension TextField { fileprivate extension TextField {
/// Layout the placeholderLabel. /// Layout the placeholderLabel.
fileprivate func layoutPlaceholderLabel() { func layoutPlaceholderLabel() {
let w = leftViewWidth + textInset let w = leftViewWidth + textInset
let h = 0 == height ? intrinsicContentSize.height : height let h = 0 == height ? intrinsicContentSize.height : height
...@@ -515,7 +515,7 @@ extension TextField { ...@@ -515,7 +515,7 @@ extension TextField {
} }
/// Layout the detailLabel. /// Layout the detailLabel.
fileprivate func layoutDetailLabel() { func layoutDetailLabel() {
let c = dividerContentEdgeInsets let c = dividerContentEdgeInsets
detailLabel.height = detailLabel.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude)).height detailLabel.height = detailLabel.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude)).height
detailLabel.x = c.left detailLabel.x = c.left
...@@ -524,12 +524,12 @@ extension TextField { ...@@ -524,12 +524,12 @@ extension TextField {
} }
/// Layout the a button. /// Layout the a button.
fileprivate func layoutButton(button: UIButton?) { func layoutButton(button: UIButton?) {
button?.frame = CGRect(x: width - height, y: 0, width: height, height: height) button?.frame = CGRect(x: width - height, y: 0, width: height, height: height)
} }
/// Layout the leftView. /// Layout the leftView.
fileprivate func layoutLeftView() { func layoutLeftView() {
guard let v = leftView else { guard let v = leftView else {
return return
} }
...@@ -540,10 +540,10 @@ extension TextField { ...@@ -540,10 +540,10 @@ extension TextField {
} }
} }
extension TextField { fileprivate extension TextField {
/// Handles the text editing did begin state. /// Handles the text editing did begin state.
@objc @objc
fileprivate func handleEditingDidBegin() { func handleEditingDidBegin() {
leftViewEditingBeginAnimation() leftViewEditingBeginAnimation()
placeholderEditingDidBeginAnimation() placeholderEditingDidBeginAnimation()
dividerEditingDidBeginAnimation() dividerEditingDidBeginAnimation()
...@@ -551,13 +551,13 @@ extension TextField { ...@@ -551,13 +551,13 @@ extension TextField {
// Live updates the textField text. // Live updates the textField text.
@objc @objc
fileprivate func handleEditingChanged(textField: UITextField) { func handleEditingChanged(textField: UITextField) {
(delegate as? TextFieldDelegate)?.textField?(textField: self, didChange: textField.text) (delegate as? TextFieldDelegate)?.textField?(textField: self, didChange: textField.text)
} }
/// Handles the text editing did end state. /// Handles the text editing did end state.
@objc @objc
fileprivate func handleEditingDidEnd() { func handleEditingDidEnd() {
leftViewEditingEndAnimation() leftViewEditingEndAnimation()
placeholderEditingDidEndAnimation() placeholderEditingDidEndAnimation()
dividerEditingDidEndAnimation() dividerEditingDidEndAnimation()
...@@ -565,7 +565,7 @@ extension TextField { ...@@ -565,7 +565,7 @@ extension TextField {
/// Handles the clearIconButton TouchUpInside event. /// Handles the clearIconButton TouchUpInside event.
@objc @objc
fileprivate func handleClearIconButton() { func handleClearIconButton() {
guard nil == delegate?.textFieldShouldClear || true == delegate?.textFieldShouldClear?(self) else { guard nil == delegate?.textFieldShouldClear || true == delegate?.textFieldShouldClear?(self) else {
return return
} }
...@@ -581,7 +581,7 @@ extension TextField { ...@@ -581,7 +581,7 @@ extension TextField {
/// Handles the visibilityIconButton TouchUpInside event. /// Handles the visibilityIconButton TouchUpInside event.
@objc @objc
fileprivate func handleVisibilityIconButton() { func handleVisibilityIconButton() {
isSecureTextEntry = !isSecureTextEntry isSecureTextEntry = !isSecureTextEntry
if !isSecureTextEntry { if !isSecureTextEntry {
......
...@@ -135,13 +135,6 @@ open class Toolbar: Bar { ...@@ -135,13 +135,6 @@ open class Toolbar: Bar {
} }
} }
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open override func prepare() { open override func prepare() {
super.prepare() super.prepare()
contentViewAlignment = .center contentViewAlignment = .center
......
...@@ -30,41 +30,21 @@ ...@@ -30,41 +30,21 @@
import UIKit import UIKit
extension UIViewController { @objc(ToolbarAlignment)
/** public enum ToolbarAlignment: Int {
case top
case bottom
}
public extension UIViewController {
/**
A convenience property that provides access to the ToolbarController. A convenience property that provides access to the ToolbarController.
This is the recommended method of accessing the ToolbarController This is the recommended method of accessing the ToolbarController
through child UIViewControllers. through child UIViewControllers.
*/ */
public var toolbarController: ToolbarController? { var toolbarController: ToolbarController? {
var viewController: UIViewController? = self return traverseViewControllerHierarchyForClassType()
while nil != viewController { }
if viewController is ToolbarController {
return viewController as? ToolbarController
}
viewController = viewController?.parent
}
return nil
}
}
@objc(ToolbarControllerDelegate)
public protocol ToolbarControllerDelegate {
/// Delegation method that executes when the floatingViewController will open.
@objc
optional func toolbarControllerWillOpenFloatingViewController(toolbarController: ToolbarController)
/// Delegation method that executes when the floatingViewController will close.
@objc
optional func toolbarControllerWillCloseFloatingViewController(toolbarController: ToolbarController)
/// Delegation method that executes when the floatingViewController did open.
@objc
optional func toolbarControllerDidOpenFloatingViewController(toolbarController: ToolbarController)
/// Delegation method that executes when the floatingViewController did close.
@objc
optional func toolbarControllerDidCloseFloatingViewController(toolbarController: ToolbarController)
} }
@objc(ToolbarController) @objc(ToolbarController)
...@@ -73,142 +53,71 @@ open class ToolbarController: StatusBarController { ...@@ -73,142 +53,71 @@ open class ToolbarController: StatusBarController {
@IBInspectable @IBInspectable
open let toolbar = Toolbar() open let toolbar = Toolbar()
/// Internal reference to the floatingViewController. /// The toolbar alignment.
private var internalFloatingViewController: UIViewController? open var toolbarAlignment = ToolbarAlignment.top {
didSet {
/// Delegation handler. layoutSubviews()
open weak var delegate: ToolbarControllerDelegate?
/// A floating UIViewController.
open var floatingViewController: UIViewController? {
get {
return internalFloatingViewController
}
set(value) {
if let v = internalFloatingViewController {
v.view.layer.rasterizationScale = Screen.scale
v.view.layer.shouldRasterize = true
delegate?.toolbarControllerWillCloseFloatingViewController?(toolbarController: self)
internalFloatingViewController = nil
UIView.animate(withDuration: 0.5,
animations: { [weak self] in
guard let s = self else {
return
}
v.view.center.y = 2 * s.view.bounds.height
s.toolbar.alpha = 1
s.rootViewController.view.alpha = 1
}) { [weak self] _ in
guard let s = self else {
return
}
v.willMove(toParentViewController: nil)
v.view.removeFromSuperview()
v.removeFromParentViewController()
v.view.layer.shouldRasterize = false
s.isUserInteractionEnabled = true
s.toolbar.isUserInteractionEnabled = true
DispatchQueue.main.async { [weak self] in
guard let s = self else {
return
}
s.delegate?.toolbarControllerDidCloseFloatingViewController?(toolbarController: s)
}
}
}
if let v = value {
// Add the noteViewController! to the view.
addChildViewController(v)
v.view.frame = view.bounds
v.view.center.y = 2 * view.bounds.height
v.view.isHidden = true
view.insertSubview(v.view, aboveSubview: toolbar)
v.view.layer.zPosition = 1500
v.didMove(toParentViewController: self)
v.view.isHidden = false
v.view.layer.rasterizationScale = Screen.scale
v.view.layer.shouldRasterize = true
view.layer.rasterizationScale = Screen.scale
view.layer.shouldRasterize = true
internalFloatingViewController = v
isUserInteractionEnabled = false
toolbar.isUserInteractionEnabled = false
delegate?.toolbarControllerWillOpenFloatingViewController?(toolbarController: self)
UIView.animate(withDuration: 0.5,
animations: { [weak self, v = v] in
guard let s = self else {
return
}
v.view.center.y = s.view.bounds.height / 2
s.toolbar.alpha = 0.5
s.rootViewController.view.alpha = 0.5
}) { [weak self, v = v] _ in
guard let s = self else {
return
}
v.view.layer.shouldRasterize = false
s.view.layer.shouldRasterize = false
DispatchQueue.main.async { [weak self] in
guard let s = self else {
return
}
s.delegate?.toolbarControllerDidOpenFloatingViewController?(toolbarController: s)
}
}
}
}
}
open override func layoutSubviews() {
super.layoutSubviews()
let y = Application.shouldStatusBarBeHidden || statusBar.isHidden ? 0 : statusBar.height
toolbar.y = y
toolbar.width = view.width
switch displayStyle {
case .partial:
let h = y + toolbar.height
rootViewController.view.y = h
rootViewController.view.height = view.height - h
case .full:
rootViewController.view.frame = view.bounds
} }
}
open override func layoutSubviews() {
super.layoutSubviews()
layoutToolbar()
layoutContainer()
layoutRootViewController()
} }
/**
Prepares the view instance when intialized. When subclassing,
it is recommended to override the prepare method
to initialize property values and other setup operations.
The super.prepare method should always be called immediately
when subclassing.
*/
open override func prepare() { open override func prepare() {
super.prepare() super.prepare()
displayStyle = .partial displayStyle = .partial
prepareStatusBar()
prepareToolbar() prepareToolbar()
} }
} }
extension ToolbarController { fileprivate extension ToolbarController {
/// Prepares the statusBar.
fileprivate func prepareStatusBar() {
shouldHideStatusBarOnRotation = false
}
/// Prepares the toolbar. /// Prepares the toolbar.
fileprivate func prepareToolbar() { func prepareToolbar() {
toolbar.zPosition = 1000
toolbar.depthPreset = .depth1 toolbar.depthPreset = .depth1
view.addSubview(toolbar) view.addSubview(toolbar)
} }
} }
fileprivate extension ToolbarController {
/// Layout the container.
func layoutContainer() {
switch displayStyle {
case .partial:
let p = toolbar.height
let q = statusBarOffsetAdjustment
let h = view.height - p - q
switch toolbarAlignment {
case .top:
container.y = q + p
container.height = h
case .bottom:
container.y = q
container.height = h
}
container.width = view.width
case .full:
container.frame = view.bounds
}
}
/// Layout the toolbar.
func layoutToolbar() {
toolbar.x = 0
toolbar.y = .top == toolbarAlignment ? statusBarOffsetAdjustment : view.height - toolbar.height
toolbar.width = view.width
}
/// Layout the rootViewController.
func layoutRootViewController() {
rootViewController.view.frame = container.bounds
}
}
...@@ -30,7 +30,40 @@ ...@@ -30,7 +30,40 @@
import UIKit import UIKit
open class RootController: UIViewController { internal extension UIViewController {
/**
Finds a view controller with a given type based on
the view controller subclass.
- Returns: An optional of type T.
*/
func traverseViewControllerHierarchyForClassType<T: UIViewController>() -> T? {
var v: UIViewController? = self
while nil != v {
if v is T {
return v as? T
}
v = v?.parent as? TransitionController
}
return Application.rootViewController?.traverseTransitionViewControllerHierarchyForClassType()
}
/**
Traverses the child view controllers to find the correct view controller type T.
- Returns: An optional of type T.
*/
func traverseTransitionViewControllerHierarchyForClassType<T: UIViewController>() -> T? {
if let v = self as? T {
return v
} else if let v = self as? TransitionController {
return v.rootViewController.traverseTransitionViewControllerHierarchyForClassType()
}
return nil
}
}
open class TransitionController: UIViewController {
/** /**
A Boolean property used to enable and disable interactivity A Boolean property used to enable and disable interactivity
with the rootViewController. with the rootViewController.
...@@ -45,14 +78,21 @@ open class RootController: UIViewController { ...@@ -45,14 +78,21 @@ open class RootController: UIViewController {
} }
} }
/// A reference to the container view.
@IBInspectable
open let container = UIView()
/** /**
A UIViewController property that references the active A UIViewController property that references the active
main UIViewController. To swap the rootViewController, it main UIViewController. To swap the rootViewController, it
is recommended to use the transitionFromRootViewController is recommended to use the transitionFromRootViewController
helper method. helper method.
*/ */
open fileprivate(set) var rootViewController: UIViewController! open internal(set) var rootViewController: UIViewController!
/// The transition type used during a transition.
open var motionTransitionType = MotionTransitionType.fade
/** /**
An initializer that initializes the object with a NSCoder object. An initializer that initializes the object with a NSCoder object.
- Parameter aDecoder: A NSCoder instance. - Parameter aDecoder: A NSCoder instance.
...@@ -88,45 +128,36 @@ open class RootController: UIViewController { ...@@ -88,45 +128,36 @@ open class RootController: UIViewController {
super.viewWillLayoutSubviews() super.viewWillLayoutSubviews()
layoutSubviews() layoutSubviews()
} }
/** /**
A method to swap rootViewController objects. A method to swap rootViewController objects.
- Parameter toViewController: The UIViewController to swap - Parameter toViewController: The UIViewController to swap
with the active rootViewController. with the active rootViewController.
- Parameter duration: A TimeInterval that sets the
animation duration of the transition.
- Parameter options: UIViewAnimationOptions thst are used
when animating the transition from the active rootViewController
to the toViewController.
- Parameter animations: An animation block that is executed during
the transition from the active rootViewController
to the toViewController.
- Parameter completion: A completion block that is execited after - Parameter completion: A completion block that is execited after
the transition animation from the active rootViewController the transition animation from the active rootViewController
to the toViewController has completed. to the toViewController has completed.
*/ */
open func transition(to viewController: UIViewController, duration: TimeInterval = 0.5, options: UIViewAnimationOptions = [], animations: (() -> Void)? = nil, completion: ((Bool) -> Void)? = nil) { open func transition(to viewController: UIViewController, completion: ((Bool) -> Void)? = nil) {
rootViewController.willMove(toParentViewController: nil) guard let fvc = rootViewController else {
addChildViewController(viewController) return
viewController.view.frame = rootViewController.view.frame }
transition(from: rootViewController,
to: viewController, let tvc = viewController
duration: duration,
options: options, tvc.view.isHidden = false
animations: animations) { [weak self, viewController = viewController, completion = completion] (result) in tvc.view.frame = container.bounds
guard let s = self else { tvc.motionModalTransitionType = motionTransitionType
return
} view.isUserInteractionEnabled = false
Motion.shared.transition(from: fvc, to: tvc, in: container) { [weak self, tvc = tvc, completion = completion] (isFinished) in
viewController.didMove(toParentViewController: s) guard let s = self else {
s.rootViewController.removeFromParentViewController() return
s.rootViewController = viewController }
s.rootViewController.view.clipsToBounds = true
s.rootViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] s.rootViewController = tvc
s.rootViewController.view.contentScaleFactor = Screen.scale s.view.isUserInteractionEnabled = true
s.view.sendSubview(toBack: s.rootViewController.view) completion?(isFinished)
completion?(result) }
}
} }
/** /**
...@@ -147,14 +178,25 @@ open class RootController: UIViewController { ...@@ -147,14 +178,25 @@ open class RootController: UIViewController {
view.clipsToBounds = true view.clipsToBounds = true
view.backgroundColor = .white view.backgroundColor = .white
view.contentScaleFactor = Screen.scale view.contentScaleFactor = Screen.scale
prepareContainer()
prepareRootViewController() prepareRootViewController()
} }
} }
extension RootController { internal extension TransitionController {
/// Prepares the container view.
func prepareContainer() {
container.frame = view.bounds
container.clipsToBounds = true
container.contentScaleFactor = Screen.scale
container.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(container)
}
/// A method that prepares the rootViewController. /// A method that prepares the rootViewController.
internal func prepareRootViewController() { func prepareRootViewController() {
prepare(viewController: rootViewController, withContainer: view) prepare(viewController: rootViewController, in: container)
} }
/** /**
...@@ -162,10 +204,10 @@ extension RootController { ...@@ -162,10 +204,10 @@ extension RootController {
the BarController within the passed in the BarController within the passed in
container view. container view.
- Parameter viewController: A UIViewController to add as a child. - Parameter viewController: A UIViewController to add as a child.
- Parameter withContainer container: A UIView that is the parent of the - Parameter in container: A UIView that is the parent of the
passed in controller view within the view hierarchy. passed in controller view within the view hierarchy.
*/ */
internal func prepare(viewController: UIViewController?, withContainer container: UIView) { func prepare(viewController: UIViewController?, in container: UIView) {
guard let v = viewController else { guard let v = viewController else {
return return
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment