Commit ce1eaaed by Daniel Dahan

updates for iOS 11 snapshots container changes

parent 0c670f8a
......@@ -39,6 +39,23 @@ internal extension CALayer {
return ($0, self.animation(forKey: $0)!.copy() as! CAAnimation)
}
}
/**
Concats transforms and returns the result.
- Parameters layer: A CALayer.
- Returns: A CATransform3D.
*/
func flatTransformTo(layer: CALayer) -> CATransform3D {
var l = layer
var t = l.transform
while let sl = l.superlayer, self != sl {
t = CATransform3DConcat(sl.transform, t)
l = sl
}
return t
}
}
public extension CALayer {
......
......@@ -34,6 +34,9 @@ fileprivate struct AssociatedInstance {
/// A boolean indicating whether Motion is enabled.
fileprivate var isEnabled: Bool
/// A boolean indicating whether Motion is enabled for subviews.
fileprivate var isEnabledForSubviews: Bool
/// An optional reference to the motion identifier.
fileprivate var identifier: String?
......@@ -52,7 +55,7 @@ fileprivate extension UIView {
fileprivate var associatedInstance: AssociatedInstance {
get {
return AssociatedObject.get(base: self, key: &AssociatedInstanceKey) {
return AssociatedInstance(isEnabled: true, identifier: nil, animations: nil, transitions: nil, alpha: 1)
return AssociatedInstance(isEnabled: true, isEnabledForSubviews: true, identifier: nil, animations: nil, transitions: nil, alpha: 1)
}
}
set(value) {
......@@ -73,6 +76,17 @@ public extension UIView {
}
}
/// A boolean that indicates whether motion is enabled.
@IBInspectable
var isMotionEnabledForSubviews: Bool {
get {
return associatedInstance.isEnabledForSubviews
}
set(value) {
associatedInstance.isEnabledForSubviews = value
}
}
/// An identifier value used to connect views across UIViewControllers.
@IBInspectable
var motionIdentifier: String? {
......@@ -195,6 +209,25 @@ internal extension UIView {
}
}
internal class SnapshotWrapperView: UIView {
let contentView: UIView
init(contentView: UIView) {
self.contentView = contentView
super.init(frame: contentView.frame)
addSubview(contentView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
contentView.bounds.size = bounds.size
contentView.center = bounds.center
}
}
internal extension UIView {
/// Retrieves a single Array of UIViews that are in the view hierarchy.
var flattenedViewHierarchy: [UIView] {
......@@ -202,11 +235,15 @@ internal extension UIView {
return []
}
if #available(iOS 9.0, *) {
return isHidden && (superview is UICollectionView || superview is UIStackView || self is UITableViewCell) ? [] : ([self] + subviews.flatMap { $0.flattenedViewHierarchy })
if #available(iOS 9.0, *), isHidden && (superview is UICollectionView || superview is UIStackView || self is UITableViewCell) {
return []
} else if isHidden && (superview is UICollectionView || self is UITableViewCell) {
return []
} else if isMotionEnabledForSubviews {
return [self] + subviews.flatMap { $0.flattenedViewHierarchy }
} else {
return [self]
}
return isHidden && (superview is UICollectionView || self is UITableViewCell) ? [] : ([self] + subviews.flatMap { $0.flattenedViewHierarchy })
}
/**
......@@ -250,8 +287,21 @@ internal extension UIView {
let imageView = UIImageView(image: image)
imageView.frame = bounds
let snapshotView = UIView(frame: bounds)
snapshotView.addSubview(imageView)
return snapshotView
return SnapshotWrapperView(contentView: imageView)
}
/**
Returns a snapshot of the view itself.
- Returns: An optional UIView.
*/
func snapshotView() -> UIView? {
let snapshot = snapshotView(afterScreenUpdates: true)
if #available(iOS 11.0, *), let oldSnapshot = snapshot {
/// iOS 11 no longer contains a container view.
return SnapshotWrapperView(contentView: oldSnapshot)
}
return snapshot
}
}
......@@ -702,15 +702,15 @@ extension Motion: UIViewControllerTransitioningDelegate {
}
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
self.isPresenting = true
self.fromViewController = fromViewController ?? presenting
self.toViewController = toViewController ?? presented
isPresenting = true
fromViewController = fromViewController ?? presenting
toViewController = toViewController ?? presented
return self
}
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
self.isPresenting = false
self.fromViewController = fromViewController ?? dismissed
isPresenting = false
fromViewController = fromViewController ?? dismissed
return self
}
......@@ -734,10 +734,10 @@ extension Motion: UIViewControllerInteractiveTransitioning {
extension Motion: UINavigationControllerDelegate {
public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
self.isPresenting = .push == operation
self.fromViewController = fromViewController ?? fromVC
self.toViewController = toViewController ?? toVC
self.isNavigationController = true
isPresenting = .push == operation
fromViewController = fromViewController ?? fromVC
toViewController = toViewController ?? toVC
isNavigationController = true
return self
}
......@@ -761,10 +761,10 @@ extension Motion: UITabBarControllerDelegate {
let fromVCIndex = tabBarController.childViewControllers.index(of: fromVC)!
let toVCIndex = tabBarController.childViewControllers.index(of: toVC)!
self.isPresenting = toVCIndex > fromVCIndex
self.fromViewController = fromViewController ?? fromVC
self.toViewController = toViewController ?? toVC
self.isTabBarController = true
isPresenting = toVCIndex > fromVCIndex
fromViewController = fromViewController ?? fromVC
toViewController = toViewController ?? toVC
isTabBarController = true
return self
}
......
......@@ -163,8 +163,8 @@ public extension MotionContext {
- Returns: A UIView.
*/
func snapshotView(for view: UIView) -> UIView {
if let v = viewToSnapshot[view] {
return v
if let snapshot = viewToSnapshot[view] {
return snapshot
}
var containerView = container
......@@ -174,27 +174,27 @@ public extension MotionContext {
case .local:
containerView = view
while containerView != container, nil == viewToSnapshot[containerView], let superview = containerView.superview {
while containerView != container, viewToSnapshot[containerView] == nil, let superview = containerView.superview {
containerView = superview
}
if let snapshot = viewToSnapshot[containerView] {
containerView = snapshot
}
case .sameParent:
containerView = view.superview!
if let visualEffectView = containerView as? UIVisualEffectView {
containerView = visualEffectView.contentView
}
case .global:
break
}
unhide(view: view)
// Capture a snapshot without the alpha & cornerRadius values.
/// Capture a snapshot without alpha & cornerRadius.
let oldCornerRadius = view.layer.cornerRadius
view.layer.cornerRadius = 0
let oldAlpha = view.alpha
view.layer.cornerRadius = 0
view.alpha = 1
let snapshot: UIView
......@@ -202,59 +202,57 @@ public extension MotionContext {
switch snapshotType {
case .normal:
snapshot = view.snapshotView(afterScreenUpdates: true)!
snapshot = view.snapshotView() ?? UIView()
case .layerRender:
snapshot = view.slowSnapshotView()
case .noSnapshot:
if nil == superviewToNoSnapshotSubviewMap[view.superview!] {
if view.superview != container {
if superviewToNoSnapshotSubviewMap[view.superview!] == nil {
superviewToNoSnapshotSubviewMap[view.superview!] = []
}
superviewToNoSnapshotSubviewMap[view.superview!]!.append((view.superview!.subviews.index(of: view)!, view))
}
snapshot = view
case .optimized:
#if os(tvOS)
snapshot = view.snapshotView(afterScreenUpdates: true)!
#else
if #available(iOS 9.0, *), let stackView = view as? UIStackView {
snapshot = stackView.slowSnapshotView()
} else if let imageView = view as? UIImageView, view.subviews.isEmpty {
let contentView = UIImageView(image: imageView.image)
contentView.frame = imageView.bounds
contentView.contentMode = imageView.contentMode
contentView.tintColor = imageView.tintColor
contentView.backgroundColor = imageView.backgroundColor
let snapShotView = UIView()
snapShotView.addSubview(contentView)
snapshot = snapShotView
} else if let barView = view as? UINavigationBar, barView.isTranslucent {
let newBarView = UINavigationBar(frame: barView.frame)
} else if let navigationBar = view as? UINavigationBar, navigationBar.isTranslucent {
let newNavigationBar = UINavigationBar(frame: navigationBar.frame)
newNavigationBar.barStyle = navigationBar.barStyle
newNavigationBar.tintColor = navigationBar.tintColor
newNavigationBar.barTintColor = navigationBar.barTintColor
newNavigationBar.clipsToBounds = false
newBarView.barStyle = barView.barStyle
newBarView.tintColor = barView.tintColor
newBarView.barTintColor = barView.barTintColor
newBarView.clipsToBounds = false
// Take a snapshot without the background.
navigationBar.layer.sublayers![0].opacity = 0
let realSnapshot = navigationBar.snapshotView(afterScreenUpdates: true)!
navigationBar.layer.sublayers![0].opacity = 1
newNavigationBar.addSubview(realSnapshot)
snapshot = newNavigationBar
// take a snapshot without the background
barView.layer.sublayers![0].opacity = 0
let realSnapshot = barView.snapshotView(afterScreenUpdates: true)!
barView.layer.sublayers![0].opacity = 1
newBarView.addSubview(realSnapshot)
snapshot = newBarView
} else if let effectView = view as? UIVisualEffectView {
snapshot = UIVisualEffectView(effect: effectView.effect)
snapshot.frame = effectView.bounds
} else {
snapshot = view.snapshotView(afterScreenUpdates: true)!
snapshot = view.snapshotView() ?? UIView()
}
#endif
}
......@@ -268,18 +266,21 @@ public extension MotionContext {
view.layer.cornerRadius = oldCornerRadius
view.alpha = oldAlpha
if .noSnapshot != snapshotType {
snapshot.layer.allowsGroupOpacity = false
snapshot.layer.anchorPoint = view.layer.anchorPoint
snapshot.layer.position = containerView.convert(view.layer.position, from: view.superview!)
snapshot.layer.transform = containerView.layer.flatTransformTo(layer: view.layer)
snapshot.layer.bounds = view.layer.bounds
snapshot.motionIdentifier = view.motionIdentifier
if snapshotType != .noSnapshot {
if !(view is UINavigationBar), let contentView = snapshot.subviews.get(0) {
/**
The snapshot's contentView must have the cornerRadius value,
since the snapshot might not have maskToBounds set
*/
// the Snapshot's contentView must have hold the cornerRadius value,
// since the snapshot might not have maskToBounds set
contentView.layer.cornerRadius = view.layer.cornerRadius
contentView.layer.masksToBounds = true
}
snapshot.layer.allowsGroupOpacity = false
snapshot.layer.cornerRadius = view.layer.cornerRadius
snapshot.layer.zPosition = view.layer.zPosition
snapshot.layer.opacity = view.layer.opacity
......@@ -288,7 +289,6 @@ public extension MotionContext {
snapshot.layer.masksToBounds = view.layer.masksToBounds
snapshot.layer.borderColor = view.layer.borderColor
snapshot.layer.borderWidth = view.layer.borderWidth
snapshot.layer.transform = view.layer.transform
snapshot.layer.contentsRect = view.layer.contentsRect
snapshot.layer.contentsScale = view.layer.contentsScale
......@@ -299,18 +299,16 @@ public extension MotionContext {
snapshot.layer.shadowOffset = view.layer.shadowOffset
snapshot.layer.shadowPath = view.layer.shadowPath
}
}
snapshot.frame = containerView.convert(view.bounds, from: view)
snapshot.motionIdentifier = view.motionIdentifier
hide(view: view)
}
if let pairedView = transitionPairedView(for: view), let pairedSnapshot = viewToSnapshot[pairedView] {
let siblingViews = pairedView.superview!.subviews
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(snapshot)
for subview in pairedView.subviews {
insertGlobalViewTree(view: subview)
......@@ -319,10 +317,11 @@ public extension MotionContext {
for sibling in nextSiblings {
insertGlobalViewTree(view: sibling)
}
} else {
containerView.addSubview(snapshot)
}
containerView.addSubview(snapshot)
viewToSnapshot[view] = snapshot
return snapshot
......
......@@ -31,5 +31,4 @@ import UIKit
public enum MotionCoordinateSpace {
case global
case local
case sameParent
}
......@@ -61,7 +61,6 @@ fileprivate extension MotionIndependentController {
- PArameter toViews: An Array of UIViews.
*/
func prepareContext(fromViews: [UIView], toViews: [UIView]) {
context.defaultCoordinateSpace = .sameParent
context.set(fromViews: fromViews, toViews: toViews)
processContext()
}
......
......@@ -515,11 +515,6 @@ public extension MotionTransition {
$0.coordinateSpace = .global
}
/// Use same parent coordinate space.
static var useSameParentCoordinateSpace = MotionTransition {
$0.coordinateSpace = .sameParent
}
/// Ignore all motion transition attributes for a view's direct subviews.
static var ignoreSubviewTransitions: MotionTransition = .ignoreSubviewTransitions()
......
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