diff --git a/Display.xcodeproj/project.pbxproj b/Display.xcodeproj/project.pbxproj index 994d9c495f..ff3255d01f 100644 --- a/Display.xcodeproj/project.pbxproj +++ b/Display.xcodeproj/project.pbxproj @@ -53,6 +53,9 @@ D06EE8451B7140FF00837186 /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06EE8441B7140FF00837186 /* Font.swift */; }; D07921A91B6FC0C0005C23D9 /* KeyboardHostWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07921A81B6FC0C0005C23D9 /* KeyboardHostWindow.swift */; }; D07921AC1B6FC92B005C23D9 /* StatusBarHostWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07921AB1B6FC92B005C23D9 /* StatusBarHostWindow.swift */; }; + D0DC48541BF93D8B00F672FD /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC48531BF93D8A00F672FD /* TabBarController.swift */; }; + D0DC48561BF945DD00F672FD /* TabBarNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC48551BF945DD00F672FD /* TabBarNode.swift */; }; + D0DC485F1BF949FB00F672FD /* TabBarContollerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC485E1BF949FB00F672FD /* TabBarContollerNode.swift */; }; D0E49C881B83A3580099E553 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E49C871B83A3580099E553 /* ImageCache.swift */; }; /* End PBXBuildFile section */ @@ -116,6 +119,9 @@ D06EE8441B7140FF00837186 /* Font.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = ""; }; D07921A81B6FC0C0005C23D9 /* KeyboardHostWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardHostWindow.swift; sourceTree = ""; }; D07921AB1B6FC92B005C23D9 /* StatusBarHostWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarHostWindow.swift; sourceTree = ""; }; + D0DC48531BF93D8A00F672FD /* TabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = ""; }; + D0DC48551BF945DD00F672FD /* TabBarNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabBarNode.swift; sourceTree = ""; }; + D0DC485E1BF949FB00F672FD /* TabBarContollerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabBarContollerNode.swift; sourceTree = ""; }; D0E49C871B83A3580099E553 /* ImageCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -173,6 +179,7 @@ D07921AA1B6FC911005C23D9 /* Status Bar */, D07921A71B6FC0AE005C23D9 /* Keyboard */, D05CC3211B695AA600E235A3 /* Navigation */, + D0DC48521BF93D7C00F672FD /* Tabs */, D02BDAEC1B6A7053008AFAD2 /* Nodes */, D0E49C861B83A1680099E553 /* Image Cache */, D05CC2A11B69326C00E235A3 /* Window.swift */, @@ -276,6 +283,16 @@ name = "Status Bar"; sourceTree = ""; }; + D0DC48521BF93D7C00F672FD /* Tabs */ = { + isa = PBXGroup; + children = ( + D0DC48531BF93D8A00F672FD /* TabBarController.swift */, + D0DC485E1BF949FB00F672FD /* TabBarContollerNode.swift */, + D0DC48551BF945DD00F672FD /* TabBarNode.swift */, + ); + name = Tabs; + sourceTree = ""; + }; D0E49C861B83A1680099E553 /* Image Cache */ = { isa = PBXGroup; children = ( @@ -419,13 +436,16 @@ D05CC2F71B6955D000E235A3 /* UIKitUtils.swift in Sources */, D05CC3161B695A9600E235A3 /* NavigationBar.swift in Sources */, D05CC31D1B695A9600E235A3 /* UIBarButtonItem+Proxy.m in Sources */, + D0DC48541BF93D8B00F672FD /* TabBarController.swift in Sources */, D05CC3171B695A9600E235A3 /* NavigationItemWrapper.swift in Sources */, D05CC3191B695A9600E235A3 /* NavigationBackButtonNode.swift in Sources */, D05CC3071B69575900E235A3 /* NSBag.m in Sources */, + D0DC48561BF945DD00F672FD /* TabBarNode.swift in Sources */, D05CC31A1B695A9600E235A3 /* NavigationButtonNode.swift in Sources */, D05CC2E71B69555800E235A3 /* CAAnimationUtils.swift in Sources */, D05CC31B1B695A9600E235A3 /* NavigationTitleNode.swift in Sources */, D05CC31C1B695A9600E235A3 /* BarButtonItemWrapper.swift in Sources */, + D0DC485F1BF949FB00F672FD /* TabBarContollerNode.swift in Sources */, D05CC2FA1B6955D000E235A3 /* UINavigationItem+Proxy.m in Sources */, D05CC2E81B69555800E235A3 /* CALayer+ImplicitAnimations.m in Sources */, D05CC2EC1B69558A00E235A3 /* RuntimeUtils.m in Sources */, @@ -549,6 +569,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -569,6 +590,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; diff --git a/Display/CAAnimationUtils.swift b/Display/CAAnimationUtils.swift index 7f4940375f..d7232bd9db 100644 --- a/Display/CAAnimationUtils.swift +++ b/Display/CAAnimationUtils.swift @@ -1,16 +1,38 @@ import UIKit @objc private class CALayerAnimationDelegate: NSObject { - let completion: Bool -> Void + var completion: (Bool -> Void)? - init(completion: Bool -> Void) { + init(completion: (Bool -> Void)?) { self.completion = completion super.init() } @objc override func animationDidStop(anim: CAAnimation, finished flag: Bool) { - self.completion(flag) + if let completion = self.completion { + completion(flag) + } + } +} + +private let completionKey = "CAAnimationUtils_completion" + +public extension CAAnimation { + public var completion: (Bool -> Void)? { + get { + if let delegate = self.delegate as? CALayerAnimationDelegate { + return delegate.completion + } else { + return nil + } + } set(value) { + if let delegate = self.delegate as? CALayerAnimationDelegate { + delegate.completion = value + } else { + self.delegate = CALayerAnimationDelegate(completion: value) + } + } } } diff --git a/Display/Font.swift b/Display/Font.swift index e908fac5f8..577522a280 100644 --- a/Display/Font.swift +++ b/Display/Font.swift @@ -3,18 +3,16 @@ import UIKit public struct Font { public static func regular(size: CGFloat) -> UIFont { - if matchMinimumSystemVersion(9) { - return UIFont(name: ".SFUIDisplay-Regular", size: size)! - } else { - return UIFont(name: "HelveticaNeue", size: size)! - } + return UIFont.systemFontOfSize(size) } public static func medium(size: CGFloat) -> UIFont { - if matchMinimumSystemVersion(9) { - return UIFont(name: ".SFUIDisplay-Medium", size: size)! - } else { - return UIFont(name: "HelveticaNeue-Medium", size: size)! - } + return UIFont.boldSystemFontOfSize(size) + } +} + +public extension NSAttributedString { + convenience init(string: String, font: CTFontRef, textColor: UIColor = UIColor.blackColor()) { + self.init(string: string, attributes: [kCTFontAttributeName as String: font, kCTForegroundColorAttributeName as String: textColor.CGColor]) } } diff --git a/Display/KeyboardHostWindow.swift b/Display/KeyboardHostWindow.swift index c6597c2a92..114a07f70c 100644 --- a/Display/KeyboardHostWindow.swift +++ b/Display/KeyboardHostWindow.swift @@ -2,7 +2,7 @@ import Foundation import UIKit public class KeyboardHostWindow: UIWindow { - let textField: UITextField + public let textField: UITextField convenience public init() { self.init(frame: CGRect()) @@ -13,7 +13,7 @@ public class KeyboardHostWindow: UIWindow { super.init(frame: frame) - self.windowLevel = 1000.0 + self.windowLevel = -1.0 self.rootViewController = UIViewController() self.addSubview(self.textField) } diff --git a/Display/NavigationBar.swift b/Display/NavigationBar.swift index 186ef84e77..00552b5332 100644 --- a/Display/NavigationBar.swift +++ b/Display/NavigationBar.swift @@ -14,6 +14,8 @@ public class NavigationBar: ASDisplayNode { private var tempItem: UINavigationItem? private var tempItemWrapper: NavigationItemWrapper? + private let stripeHeight: CGFloat = 1.0 / UIScreen.mainScreen().scale + var backPressed: () -> () = { } private var collapsed: Bool { @@ -112,7 +114,8 @@ public class NavigationBar: ASDisplayNode { } public override func layout() { - self.stripeView.frame = CGRect(x: 0.0, y: self.frame.size.height - 0.5, width: self.frame.size.width, height: 0.5) + + self.stripeView.frame = CGRect(x: 0.0, y: self.frame.size.height - stripeHeight, width: self.frame.size.width, height: stripeHeight) self.topItemWrapper?.layoutItems() self.tempItemWrapper?.layoutItems() diff --git a/Display/NavigationController.swift b/Display/NavigationController.swift index e6a852f8ba..e173ccce45 100644 --- a/Display/NavigationController.swift +++ b/Display/NavigationController.swift @@ -3,12 +3,22 @@ import UIKit import AsyncDisplayKit import SwiftSignalKit +private struct NavigationControllerLayout { + let layout: ViewControllerLayout + let statusBarHeight: CGFloat +} + public class NavigationController: NavigationControllerProxy, WindowContentController, UIGestureRecognizerDelegate { private var _navigationBar: NavigationBar! private var navigationTransitionCoordinator: NavigationTransitionCoordinator? private var currentPushDisposable = MetaDisposable() + private var statusBarChangeObserver: AnyObject? + + private var layout: NavigationControllerLayout? + private var pendingLayout: (NavigationControllerLayout, NSTimeInterval, Bool)? + public override init() { self._navigationBar = nil @@ -26,6 +36,23 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr } return } + + self.statusBarChangeObserver = NSNotificationCenter.defaultCenter().addObserverForName(UIApplicationWillChangeStatusBarFrameNotification, object: nil, queue: NSOperationQueue.mainQueue(), usingBlock: { [weak self] notification in + if let strongSelf = self { + let statusBarHeight: CGFloat = (notification.userInfo?[UIApplicationStatusBarFrameUserInfoKey] as? NSValue)?.CGRectValue().height ?? 20.0 + + let previousLayout: NavigationControllerLayout? + if let pendingLayout = strongSelf.pendingLayout { + previousLayout = pendingLayout.0 + } else { + previousLayout = strongSelf.layout + } + + strongSelf.pendingLayout = (NavigationControllerLayout(layout: ViewControllerLayout(size: previousLayout?.layout.size ?? CGSize(), insets: previousLayout?.layout.insets ?? UIEdgeInsets(), inputViewHeight: 0.0), statusBarHeight: statusBarHeight), (strongSelf.pendingLayout?.2 ?? false) ? (strongSelf.pendingLayout?.1 ?? 0.3) : max(strongSelf.pendingLayout?.1 ?? 0.0, 0.35), true) + + strongSelf.view.setNeedsLayout() + } + }) } public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { @@ -42,8 +69,6 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr self.navigationBar.superview?.insertSubview(_navigationBar.view, aboveSubview: self.navigationBar) self.navigationBar.removeFromSuperview() - self._navigationBar.frame = navigationBarFrame(self.view.frame.size) - let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: Selector("panGesture:")) panRecognizer.delegate = self panRecognizer.cancelsTouchesInView = true @@ -152,14 +177,19 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr } } - public func pushViewController(signal: Signal) -> Disposable { - let disposable = (signal |> deliverOnMainQueue).start(next: {[weak self] controller in + public func pushViewController(controller: ViewController) { + let layout: NavigationControllerLayout + if let currentLayout = self.layout { + layout = currentLayout + } else { + layout = NavigationControllerLayout(layout: ViewControllerLayout(size: self.view.bounds.size, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0), inputViewHeight: 0.0), statusBarHeight: 20.0) + } + controller.setParentLayout(self.childControllerLayoutForLayout(layout), duration: 0.0, curve: 0) + self.currentPushDisposable.set((controller.ready.get() |> take(1)).start(next: {[weak self] _ in if let strongSelf = self { strongSelf.pushViewController(controller, animated: true) } - }) - self.currentPushDisposable.set(disposable) - return disposable + })) } public override func pushViewController(viewController: UIViewController, animated: Bool) { @@ -186,7 +216,14 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr let topViewController = viewControllers[viewControllers.count - 1] as UIViewController if let controller = topViewController as? WindowContentController { - controller.setViewSize(self.view.bounds.size, insets: UIEdgeInsets(top: CGRectGetMaxY(self._navigationBar.frame), left: 0.0, bottom: 0.0, right: 0.0), duration: 0.0) + let layout: NavigationControllerLayout + if let currentLayout = self.layout { + layout = currentLayout + } else { + layout = NavigationControllerLayout(layout: ViewControllerLayout(size: self.view.bounds.size, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0), inputViewHeight: 0.0), statusBarHeight: 20.0) + } + + controller.setParentLayout(self.childControllerLayoutForLayout(layout), duration: 0.0, curve: 0) } else { topViewController.view.frame = CGRect(origin: CGPoint(), size: self.view.bounds.size) } @@ -195,50 +232,78 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr super.setViewControllers(viewControllers, animated: animated) } - private func navigationBarFrame(size: CGSize) -> CGRect { - //let condensedBar = (size.height < size.width || size.height <= 320.0) && size.height < 768.0 - return CGRect(x: 0.0, y: 0.0, width: size.width, height: 20.0 + (size.height >= size.width ? 44.0 : 32.0)) + private func navigationBarFrame(layout: NavigationControllerLayout) -> CGRect { + return CGRect(x: 0.0, y: layout.statusBarHeight - 20.0, width: layout.layout.size.width, height: 20.0 + (layout.layout.size.height >= layout.layout.size.width ? 44.0 : 32.0)) } - public func setViewSize(size: CGSize, insets: UIEdgeInsets, duration: NSTimeInterval) { - if duration > DBL_EPSILON { - animateRotation(self.view, toFrame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height), duration: duration) - } - else { - self.view.frame = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height) + private func childControllerLayoutForLayout(layout: NavigationControllerLayout) -> ViewControllerLayout { + var insets = layout.layout.insets + insets.top = self.navigationBarFrame(layout).maxY + return ViewControllerLayout(size: layout.layout.size, insets: insets, inputViewHeight: 0.0) + } + + public func setParentLayout(layout: ViewControllerLayout, duration: NSTimeInterval, curve: UInt) { + let previousLayout: NavigationControllerLayout? + if let pendingLayout = self.pendingLayout { + previousLayout = pendingLayout.0 + } else { + previousLayout = self.layout } - if duration > DBL_EPSILON { - animateRotation(self._navigationBar, toFrame: self.navigationBarFrame(size), duration: duration) - } - else { - self._navigationBar.frame = self.navigationBarFrame(size) - } + self.pendingLayout = (NavigationControllerLayout(layout: layout, statusBarHeight: previousLayout?.statusBarHeight ?? 20.0), duration, false) - if let navigationTransitionCoordinator = self.navigationTransitionCoordinator { - //navigationTransitionView.frame = CGRectMake(0.0, 0.0, toSize.width, toSize.height) + self.view.setNeedsLayout() + } + + public override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + if let pendingLayout = self.pendingLayout { + self.layout = pendingLayout.0 - if self.viewControllers.count >= 2 { - let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController + if pendingLayout.1 > DBL_EPSILON { + animateRotation(self.view, toFrame: CGRect(x: 0.0, y: 0.0, width: pendingLayout.0.layout.size.width, height: pendingLayout.0.layout.size.height), duration: pendingLayout.1) + } + else { + self.view.frame = CGRect(x: 0.0, y: 0.0, width: pendingLayout.0.layout.size.width, height: pendingLayout.0.layout.size.height) + } + + if pendingLayout.1 > DBL_EPSILON { + animateRotation(self._navigationBar, toFrame: self.navigationBarFrame(pendingLayout.0), duration: pendingLayout.1) + } + else { + self._navigationBar.frame = self.navigationBarFrame(pendingLayout.0) + } + + if let navigationTransitionCoordinator = self.navigationTransitionCoordinator { + //navigationTransitionView.frame = CGRectMake(0.0, 0.0, toSize.width, toSize.height) - if let controller = bottomController as? WindowContentController { - controller.setViewSize(size, insets: UIEdgeInsets(top: CGRectGetMaxY(self._navigationBar.frame), left: 0.0, bottom: 0.0, right: 0.0), duration: duration) + if self.viewControllers.count >= 2 { + let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController + + if let controller = bottomController as? WindowContentController { + controller.setParentLayout(self.childControllerLayoutForLayout(pendingLayout.0), duration: pendingLayout.1, curve: 0) + } else { + bottomController.view.frame = CGRectMake(0.0, 0.0, pendingLayout.0.layout.size.width, pendingLayout.0.layout.size.height) + } + } + + self._navigationBar.setInteractivePopProgress(navigationTransitionCoordinator.progress) + } + + if let topViewController = self.topViewController { + if let controller = topViewController as? WindowContentController { + controller.setParentLayout(self.childControllerLayoutForLayout(pendingLayout.0), duration: pendingLayout.1, curve: 0) } else { - bottomController.view.frame = CGRectMake(0.0, 0.0, size.width, size.height) + topViewController.view.frame = CGRectMake(0.0, 0.0, pendingLayout.0.layout.size.width, pendingLayout.0.layout.size.height) } } - } - - if let topViewController = self.topViewController { - if let controller = topViewController as? WindowContentController { - controller.setViewSize(size, insets: UIEdgeInsets(top: CGRectGetMaxY(self._navigationBar.frame), left: 0.0, bottom: 0.0, right: 0.0), duration: duration) - } else { - topViewController.view.frame = CGRectMake(0.0, 0.0, size.width, size.height) + + if let navigationTransitionCoordinator = self.navigationTransitionCoordinator { + navigationTransitionCoordinator.updateProgress() } - } - - if let navigationTransitionCoordinator = self.navigationTransitionCoordinator { - navigationTransitionCoordinator.updateProgress() + + self.pendingLayout = nil } } diff --git a/Display/NotificationCenterUtils.h b/Display/NotificationCenterUtils.h index 09409f3fc7..6918561d91 100644 --- a/Display/NotificationCenterUtils.h +++ b/Display/NotificationCenterUtils.h @@ -1,6 +1,6 @@ #import -typedef bool (^NotificationHandlerBlock)(NSString *, id, NSDictionary *); +typedef bool (^NotificationHandlerBlock)(NSString *, id, NSDictionary *, void (^)()); @interface NotificationCenterUtils : NSObject diff --git a/Display/NotificationCenterUtils.m b/Display/NotificationCenterUtils.m index 7088967d19..d1b1aa9bff 100644 --- a/Display/NotificationCenterUtils.m +++ b/Display/NotificationCenterUtils.m @@ -1,13 +1,12 @@ #import "NotificationCenterUtils.h" #import "RuntimeUtils.h" +#import -static NSMutableArray *notificationHandlers() -{ +static NSMutableArray *notificationHandlers() { static NSMutableArray *array = nil; static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^ - { + dispatch_once(&onceToken, ^{ array = [[NSMutableArray alloc] init]; }); return array; @@ -23,8 +22,11 @@ static NSMutableArray *notificationHandlers() { for (NotificationHandlerBlock handler in notificationHandlers()) { - if (handler(aName, anObject, aUserInfo)) + if (handler(aName, anObject, aUserInfo, ^{ + [self _a65afc19_postNotificationName:aName object:anObject userInfo:aUserInfo]; + })) { return; + } } [self _a65afc19_postNotificationName:aName object:anObject userInfo:aUserInfo]; @@ -32,19 +34,34 @@ static NSMutableArray *notificationHandlers() @end +@interface CATransaction (Swizzle) + ++ (void)swizzle_flush; + +@end + +@implementation CATransaction (Swizzle) + ++ (void)swizzle_flush { + //printf("===flush\n"); + + [self swizzle_flush]; +} + +@end + @implementation NotificationCenterUtils -+ (void)load -{ ++ (void)load { static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^ - { + dispatch_once(&onceToken, ^{ [RuntimeUtils swizzleInstanceMethodOfClass:[NSNotificationCenter class] currentSelector:@selector(postNotificationName:object:userInfo:) newSelector:@selector(_a65afc19_postNotificationName:object:userInfo:)]; + + //[RuntimeUtils swizzleClassMethodOfClass:[CATransaction class] currentSelector:@selector(flush) newSelector:@selector(swizzle_flush)]; }); } -+ (void)addNotificationHandler:(bool (^)(NSString *, id, NSDictionary *))handler -{ ++ (void)addNotificationHandler:(NotificationHandlerBlock)handler { [notificationHandlers() addObject:[handler copy]]; } diff --git a/Display/RuntimeUtils.h b/Display/RuntimeUtils.h index 8f6e180282..6742a0f49d 100644 --- a/Display/RuntimeUtils.h +++ b/Display/RuntimeUtils.h @@ -8,6 +8,7 @@ typedef enum { @interface RuntimeUtils : NSObject + (void)swizzleInstanceMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector; ++ (void)swizzleClassMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector; @end diff --git a/Display/RuntimeUtils.m b/Display/RuntimeUtils.m index da122a4a38..3302e14754 100644 --- a/Display/RuntimeUtils.m +++ b/Display/RuntimeUtils.m @@ -4,20 +4,34 @@ @implementation RuntimeUtils -+ (void)swizzleInstanceMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector -{ ++ (void)swizzleInstanceMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector { Method origMethod = nil, newMethod = nil; origMethod = class_getInstanceMethod(targetClass, currentSelector); newMethod = class_getInstanceMethod(targetClass, newSelector); - if ((origMethod != nil) && (newMethod != nil)) - { - if(class_addMethod(targetClass, currentSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) - { + if ((origMethod != nil) && (newMethod != nil)) { + if(class_addMethod(targetClass, currentSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { class_replaceMethod(targetClass, newSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); - } - else + } else { method_exchangeImplementations(origMethod, newMethod); + } + } +} + ++ (void)swizzleClassMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector { + Method origMethod = nil, newMethod = nil; + + origMethod = class_getClassMethod(targetClass, currentSelector); + newMethod = class_getClassMethod(targetClass, newSelector); + + targetClass = object_getClass((id)targetClass); + + if ((origMethod != nil) && (newMethod != nil)) { + if(class_addMethod(targetClass, currentSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { + class_replaceMethod(targetClass, newSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); + } else { + method_exchangeImplementations(origMethod, newMethod); + } } } diff --git a/Display/TabBarContollerNode.swift b/Display/TabBarContollerNode.swift new file mode 100644 index 0000000000..336a063fe8 --- /dev/null +++ b/Display/TabBarContollerNode.swift @@ -0,0 +1,37 @@ +import Foundation +import AsyncDisplayKit + +class TabBarControllerNode: ASDisplayNode { + let tabBarNode: TabBarNode + + var currentControllerView: UIView? { + didSet { + oldValue?.removeFromSuperview() + + if let currentControllerView = self.currentControllerView { + self.view.insertSubview(currentControllerView, atIndex: 0) + } + } + } + + override init() { + self.tabBarNode = TabBarNode() + + super.init() + + self.addSubnode(self.tabBarNode) + } + + func updateLayout(layout: ViewControllerLayout, previousLayout: ViewControllerLayout?, duration: Double, curve: UInt) { + let update = { + self.tabBarNode.frame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - layout.insets.bottom - 49.0), size: CGSize(width: layout.size.width, height: 49.0)) + self.tabBarNode.layout() + } + + if duration > DBL_EPSILON { + UIView.animateWithDuration(duration, delay: 0.0, options: UIViewAnimationOptions(rawValue: curve << 7), animations: update, completion: nil) + } else { + update() + } + } +} diff --git a/Display/TabBarController.swift b/Display/TabBarController.swift new file mode 100644 index 0000000000..0afe63998f --- /dev/null +++ b/Display/TabBarController.swift @@ -0,0 +1,101 @@ +import Foundation +import UIKit +import AsyncDisplayKit + +public class TabBarController: ViewController { + private var tabBarControllerNode: TabBarControllerNode { + get { + return super.displayNode as! TabBarControllerNode + } + } + + public var controllers: [ViewController] = [] { + didSet { + self.tabBarControllerNode.tabBarNode.tabBarItems = self.controllers.map({ $0.tabBarItem }) + + if oldValue.count == 0 && self.controllers.count != 0 { + self.updateSelectedIndex() + } + } + } + + private var _selectedIndex: Int = 1 + public var selectedIndex: Int { + get { + return _selectedIndex + } set(value) { + let index = max(0, min(self.controllers.count - 1, value)) + if _selectedIndex != index { + _selectedIndex = index + + self.updateSelectedIndex() + } + } + } + + private var layout: ViewControllerLayout? + private var currentController: ViewController? + + override public init() { + super.init() + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override public func loadDisplayNode() { + self.displayNode = TabBarControllerNode() + + self.updateSelectedIndex() + } + + private func updateSelectedIndex() { + if !self.isNodeLoaded { + return + } + + if let currentController = self.currentController { + currentController.willMoveToParentViewController(nil) + self.tabBarControllerNode.currentControllerView = nil + currentController.removeFromParentViewController() + currentController.didMoveToParentViewController(nil) + + self.currentController = nil + } + + if self._selectedIndex < self.controllers.count { + self.currentController = self.controllers[self._selectedIndex] + } + + if let currentController = self.currentController { + currentController.willMoveToParentViewController(self) + if let layout = self.layout { + currentController.view.frame = CGRect(origin: CGPoint(), size: layout.size) + + currentController.setParentLayout(self.childControllerLayoutForLayout(layout), duration: 0.0, curve: 0) + } + self.tabBarControllerNode.currentControllerView = currentController.view + self.addChildViewController(currentController) + currentController.didMoveToParentViewController(self) + } + } + + private func childControllerLayoutForLayout(layout: ViewControllerLayout) -> ViewControllerLayout { + var insets = layout.insets + insets.bottom += 49.0 + return ViewControllerLayout(size: layout.size, insets: insets, inputViewHeight: layout.inputViewHeight) + } + + override public func updateLayout(layout: ViewControllerLayout, previousLayout: ViewControllerLayout?, duration: Double, curve: UInt) { + super.updateLayout(layout, previousLayout: previousLayout, duration: duration, curve: curve) + + self.tabBarControllerNode.updateLayout(layout, previousLayout: previousLayout, duration: duration, curve: curve) + + self.layout = layout + if let currentController = self.currentController { + currentController.view.frame = CGRect(origin: CGPoint(), size: layout.size) + currentController.setParentLayout(self.childControllerLayoutForLayout(layout), duration: duration, curve: curve) + } + } +} diff --git a/Display/TabBarNode.swift b/Display/TabBarNode.swift new file mode 100644 index 0000000000..93031028ab --- /dev/null +++ b/Display/TabBarNode.swift @@ -0,0 +1,106 @@ +import Foundation +import UIKit +import AsyncDisplayKit + +private let separatorHeight: CGFloat = 1.0 / UIScreen.mainScreen().scale +private func tabBarItemImage(image: UIImage?, title: String, tintColor: UIColor) -> UIImage { + let font = Font.regular(10.0) + let titleSize = (title as NSString).boundingRectWithSize(CGSize(width: CGFloat.max, height: CGFloat.max), options: [.UsesLineFragmentOrigin], attributes: [NSFontAttributeName: font], context: nil).size + + let imageSize: CGSize + if let image = image { + imageSize = image.size + } else { + imageSize = CGSize() + } + + let size = CGSize(width: max(ceil(titleSize.width), imageSize.width), height: 45.0) + + UIGraphicsBeginImageContextWithOptions(size, true, 0.0) + let context = UIGraphicsGetCurrentContext() + + CGContextSetFillColorWithColor(context, UIColor(0xf7f7f7).CGColor) + CGContextFillRect(context, CGRect(origin: CGPoint(), size: size)) + + image?.drawAtPoint(CGPoint(x: floorToScreenPixels((size.width - imageSize.width) / 2.0), y: 0.0)) + + (title as NSString).drawAtPoint(CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: size.height - titleSize.height - 3.0), withAttributes: [NSFontAttributeName: font]) + + CGContextSetBlendMode(context, .SourceIn) + CGContextSetFillColorWithColor(context, tintColor.CGColor) + //CGContextFillRect(context, CGRect(origin: CGPoint(), size: size)) + + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return image +} + +class TabBarNode: ASDisplayNode { + let separatorNode: ASDisplayNode + + var tabBarNodes: [ASImageNode] = [] + + var tabBarItems: [UITabBarItem] = [] { + didSet { + self.reloadTabBarItems() + } + } + + override init() { + self.separatorNode = ASDisplayNode() + self.separatorNode.backgroundColor = UIColor(0xb2b2b2) + self.separatorNode.opaque = true + self.separatorNode.layerBacked = true + + super.init() + + self.opaque = true + self.backgroundColor = UIColor(0xf7f7f7) + + self.addSubnode(self.separatorNode) + } + + private func reloadTabBarItems() { + for node in self.tabBarNodes { + node.removeFromSupernode() + } + + var tabBarNodes: [ASImageNode] = [] + for item in self.tabBarItems { + let node = ASImageNode() + node.displaysAsynchronously = false + node.displayWithoutProcessing = true + node.layerBacked = true + node.image = tabBarItemImage(item.image, title: item.title ?? "", tintColor: UIColor.blueColor()) + tabBarNodes.append(node) + self.addSubnode(node) + } + + self.tabBarNodes = tabBarNodes + + self.setNeedsLayout() + } + + override func layout() { + super.layout() + + let size = self.bounds.size + + self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -separatorHeight), size: CGSize(width: size.width, height: separatorHeight)) + + if self.tabBarNodes.count != 0 { + let distanceBetweenNodes = size.width / CGFloat(self.tabBarNodes.count) + + let internalWidth = distanceBetweenNodes * CGFloat(self.tabBarNodes.count - 1) + let leftNodeOriginX = (size.width - internalWidth) / 2.0 + + for i in 0 ..< self.tabBarNodes.count { + let node = self.tabBarNodes[i] + node.measure(CGSize(width: internalWidth, height: size.height)) + + node.frame = CGRect(origin: CGPoint(x: floor(leftNodeOriginX + CGFloat(i) * distanceBetweenNodes - node.calculatedSize.width / 2.0), y: 4.0), size: node.calculatedSize) + } + } + } +} \ No newline at end of file diff --git a/Display/UIKitUtils.h b/Display/UIKitUtils.h index d53da326e3..08dd3954dc 100644 --- a/Display/UIKitUtils.h +++ b/Display/UIKitUtils.h @@ -5,3 +5,9 @@ + (double)animationDurationFactor; @end + +@interface CASpringAnimation (AnimationUtils) + +- (CGFloat)valueAt:(CGFloat)t; + +@end \ No newline at end of file diff --git a/Display/UIKitUtils.m b/Display/UIKitUtils.m index c8cbb8aed8..2c9fef85ea 100644 --- a/Display/UIKitUtils.m +++ b/Display/UIKitUtils.m @@ -16,3 +16,17 @@ UIKIT_EXTERN float UIAnimationDragCoefficient(); // UIKit private drag coeffient } @end + +@interface CASpringAnimation () + +- (float)_solveForInput:(float)arg1; + +@end + +@implementation CASpringAnimation (AnimationUtils) + +- (CGFloat)valueAt:(CGFloat)t { + return [self _solveForInput:t]; +} + +@end \ No newline at end of file diff --git a/Display/UIKitUtils.swift b/Display/UIKitUtils.swift index 82d70d8eea..94d576f34d 100644 --- a/Display/UIKitUtils.swift +++ b/Display/UIKitUtils.swift @@ -26,8 +26,9 @@ private func dumpLayers(layer: CALayer, indent: String = "") { } } +private let screenScale = UIScreen.mainScreen().scale public func floorToScreenPixels(value: CGFloat) -> CGFloat { - return floor(value * 2.0) / 2.0 + return floor(value * screenScale) / screenScale } public extension UIColor { @@ -61,3 +62,16 @@ public extension CGSize { public func assertNotOnMainThread(file: String = __FILE__, line: Int = __LINE__) { assert(!NSThread.isMainThread(), "\(file):\(line) running on main thread") } + +public extension UIImage { + public func precomposed() -> UIImage { + UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale) + self.drawAtPoint(CGPoint()) + let result = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext() + if !UIEdgeInsetsEqualToEdgeInsets(self.capInsets, UIEdgeInsetsZero) { + return result.resizableImageWithCapInsets(self.capInsets, resizingMode: self.resizingMode) + } + return result + } +} diff --git a/Display/UIWindow+OrientationChange.h b/Display/UIWindow+OrientationChange.h index 3a48232217..d985458ad0 100644 --- a/Display/UIWindow+OrientationChange.h +++ b/Display/UIWindow+OrientationChange.h @@ -3,6 +3,10 @@ @interface UIWindow (OrientationChange) - (bool)isRotating; ++ (void)addPostDeviceOrientationDidChangeBlock:(void (^)())block; ++ (bool)isDeviceRotating; + +- (void)_updateToInterfaceOrientation:(int)arg1 duration:(double)arg2 force:(BOOL)arg3; @end diff --git a/Display/UIWindow+OrientationChange.m b/Display/UIWindow+OrientationChange.m index 1c7946f765..f44d682e5e 100644 --- a/Display/UIWindow+OrientationChange.m +++ b/Display/UIWindow+OrientationChange.m @@ -5,21 +5,27 @@ static const void *isRotatingKey = &isRotatingKey; +static NSMutableArray *postDeviceDidChangeOrientationBlocks() { + static NSMutableArray *array = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + array = [[NSMutableArray alloc] init]; + }); + return array; +} + +static bool _isDeviceRotating = false; + @implementation UIWindow (OrientationChange) -+ (void)load -{ ++ (void)load { static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^ - { - [NotificationCenterUtils addNotificationHandler:^bool(NSString *name, id object, NSDictionary *userInfo) - { - if ([name isEqualToString:@"UIWindowWillRotateNotification"]) - { + dispatch_once(&onceToken, ^ { + [NotificationCenterUtils addNotificationHandler:^bool(NSString *name, id object, NSDictionary *userInfo, void (^passNotification)()) { + if ([name isEqualToString:@"UIWindowWillRotateNotification"]) { [(UIWindow *)object setRotating:true]; - if (NSClassFromString(@"NSUserActivity") == NULL) - { + if (NSClassFromString(@"NSUserActivity") == NULL) { UIInterfaceOrientation orientation = [userInfo[@"UIWindowNewOrientationUserInfoKey"] integerValue]; CGSize screenSize = [UIScreen mainScreen].bounds.size; if (screenSize.width > screenSize.height) @@ -61,10 +67,32 @@ static const void *isRotatingKey = &isRotatingKey; ((UIWindow *)object).bounds = CGRectMake(0.0f, 0.0f, windowSize.width, windowSize.height); }]; } - } - else if ([name isEqualToString:@"UIWindowDidRotateNotification"]) - { + + passNotification(); + + return true; + } else if ([name isEqualToString:@"UIWindowDidRotateNotification"]) { [(UIWindow *)object setRotating:false]; + } else if ([name isEqualToString:UIDeviceOrientationDidChangeNotification]) { + //NSLog(@"notification start: %@", name); + + _isDeviceRotating = true; + + passNotification(); + + if (postDeviceDidChangeOrientationBlocks().count != 0) { + NSArray *blocks = [postDeviceDidChangeOrientationBlocks() copy]; + [postDeviceDidChangeOrientationBlocks() removeAllObjects]; + for (dispatch_block_t block in blocks) { + block(); + } + } + + _isDeviceRotating = false; + + //NSLog(@"notification end: %@", name); + + return true; } return false; @@ -72,6 +100,10 @@ static const void *isRotatingKey = &isRotatingKey; }); } ++ (void)addPostDeviceOrientationDidChangeBlock:(void (^)())block { + [postDeviceDidChangeOrientationBlocks() addObject:[block copy]]; +} + - (void)setRotating:(bool)rotating { [self setAssociatedObject:@(rotating) forKey:isRotatingKey]; @@ -82,4 +114,8 @@ static const void *isRotatingKey = &isRotatingKey; return [[self associatedObjectForKey:isRotatingKey] boolValue]; } ++ (bool)isDeviceRotating { + return _isDeviceRotating; +} + @end diff --git a/Display/ViewController.swift b/Display/ViewController.swift index fc83869639..c74892c441 100644 --- a/Display/ViewController.swift +++ b/Display/ViewController.swift @@ -1,10 +1,15 @@ import Foundation import UIKit import AsyncDisplayKit +import SwiftSignalKit + +public func ==(lhs: ViewControllerLayout, rhs: ViewControllerLayout) -> Bool { + return lhs.size == rhs.size && lhs.insets == rhs.insets && lhs.inputViewHeight == rhs.inputViewHeight +} @objc public class ViewController: UIViewController, WindowContentController { private var _displayNode: ASDisplayNode? - public var displayNode: ASDisplayNode { + public final var displayNode: ASDisplayNode { get { if let value = self._displayNode { return value @@ -22,30 +27,133 @@ import AsyncDisplayKit } } + public final var isNodeLoaded: Bool { + return self._displayNode != nil + } + + private let _ready = Promise(true) + public var ready: Promise { + return self._ready + } + + private var updateLayoutOnLayout: (ViewControllerLayout, NSTimeInterval, UInt)? + private var layout: ViewControllerLayout? + + var keyboardFrameObserver: AnyObject? + public init() { super.init(nibName: nil, bundle: nil) self.automaticallyAdjustsScrollViewInsets = false + + self.keyboardFrameObserver = NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardWillChangeFrameNotification, object: nil, queue: nil, usingBlock: { [weak self] notification in + if let strongSelf = self, _ = strongSelf._displayNode { + let keyboardFrame: CGRect = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() ?? CGRect() + let keyboardHeight = max(0.0, UIScreen.mainScreen().bounds.size.height - keyboardFrame.minY) + let duration: Double = (notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0.0 + let curve: UInt = (notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber)?.unsignedIntegerValue ?? UInt(7 << 16) + + let previousLayout: ViewControllerLayout? + var previousDurationAndCurve: (NSTimeInterval, UInt)? + if let updateLayoutOnLayout = strongSelf.updateLayoutOnLayout { + previousLayout = updateLayoutOnLayout.0 + previousDurationAndCurve = (updateLayoutOnLayout.1, updateLayoutOnLayout.2) + } else{ + previousLayout = strongSelf.layout + } + let layout = ViewControllerLayout(size: previousLayout?.size ?? CGSize(), insets: previousLayout?.insets ?? UIEdgeInsets(), inputViewHeight: keyboardHeight) + let updated: Bool + if let previousLayout = previousLayout { + updated = previousLayout != layout + } else { + updated = true + } + if updated { + print("keyboard layout change: \(layout) rotating: \(strongSelf.view.window?.isRotating())") + + let durationAndCurve: (NSTimeInterval, UInt) = previousDurationAndCurve ?? (duration > DBL_EPSILON ? 0.5 : 0.0, curve) + strongSelf.updateLayoutOnLayout = (layout, durationAndCurve.0, durationAndCurve.1) + strongSelf.view.setNeedsLayout() + } + } + }) } required public init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + deinit { + if let keyboardFrameObserver = keyboardFrameObserver { + NSNotificationCenter.defaultCenter().removeObserver(keyboardFrameObserver) + } + } + public override func loadView() { self.view = self.displayNode.view } public func loadDisplayNode() { + self.displayNode = ASDisplayNode() } - public func setViewSize(size: CGSize, insets: UIEdgeInsets, duration: NSTimeInterval) { - if duration > DBL_EPSILON { - animateRotation(self.displayNode, toFrame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height), duration: duration) + public func setParentLayout(layout: ViewControllerLayout, duration: NSTimeInterval, curve: UInt) { + if self._displayNode == nil { + self.loadDisplayNode() } - else { - self.displayNode.frame = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height) + let previousLayout: ViewControllerLayout? + if let updateLayoutOnLayout = self.updateLayoutOnLayout { + previousLayout = updateLayoutOnLayout.0 + } else { + previousLayout = self.layout + } + + let layout = ViewControllerLayout(size: layout.size, insets: layout.insets, inputViewHeight: previousLayout?.inputViewHeight ?? 0.0) + let updated: Bool + if let previousLayout = previousLayout { + updated = previousLayout != layout + } else { + updated = true + } + if updated { + if previousLayout == nil { + self.layout = layout + self.updateLayout(layout, previousLayout: previousLayout, duration: duration, curve: 0) + } else { + self.updateLayoutOnLayout = (layout, duration, 0) + self.view.setNeedsLayout() + } + } + } + + public func updateLayout(layout: ViewControllerLayout, previousLayout: ViewControllerLayout?, duration: Double, curve: UInt) { + } + + override public func viewDidLayoutSubviews() { + if let updateLayoutOnLayout = self.updateLayoutOnLayout { + if !Window.isDeviceRotating() { + if !((self.view.window as? Window)?.isUpdatingOrientationLayout ?? false) { + //print("\(self) apply inputHeight: \(updateLayoutOnLayout.0.inputViewHeight)") + let previousLayout = self.layout + self.layout = updateLayoutOnLayout.0 + self.updateLayout(updateLayoutOnLayout.0, previousLayout: previousLayout, duration: updateLayoutOnLayout.1, curve: updateLayoutOnLayout.2) + + self.updateLayoutOnLayout = nil + } else { + (self.view.window as? Window)?.addPostUpdateToInterfaceOrientationBlock({ [weak self] in + if let strongSelf = self { + strongSelf.view.setNeedsLayout() + } + }) + } + } else { + Window.addPostDeviceOrientationDidChangeBlock({ [weak self] in + if let strongSelf = self { + strongSelf.view.setNeedsLayout() + } + }) + } } } } diff --git a/Display/Window.swift b/Display/Window.swift index 2fdf47ae8f..4fccbff987 100644 --- a/Display/Window.swift +++ b/Display/Window.swift @@ -11,9 +11,14 @@ public class WindowRootViewController: UIViewController { } } -@objc +public struct ViewControllerLayout: Equatable { + public let size: CGSize + public let insets: UIEdgeInsets + public let inputViewHeight: CGFloat +} + public protocol WindowContentController { - func setViewSize(size: CGSize, insets: UIEdgeInsets, duration: NSTimeInterval) + func setParentLayout(layout: ViewControllerLayout, duration: NSTimeInterval, curve: UInt) var view: UIView! { get } } @@ -28,7 +33,9 @@ public func animateRotation(view: UIView?, toFrame: CGRect, duration: NSTimeInte public func animateRotation(view: ASDisplayNode?, toFrame: CGRect, duration: NSTimeInterval) { if let view = view { CALayer.beginRecordingChanges() - view.frame = toFrame + UIView.animateWithDuration(duration, animations: { () -> Void in + view.frame = toFrame + }) view.layout() let states = CALayer.endRecordingChanges() as! [CALayerAnimation] let k = Float(UIView.animationDurationFactor()) @@ -67,13 +74,26 @@ public func animateRotation(view: ASDisplayNode?, toFrame: CGRect, duration: NST } public class Window: UIWindow { + //public let textField: UITextField + + private var updateViewSizeOnLayout: (Bool, NSTimeInterval) = (false, 0.0) + public var isUpdatingOrientationLayout = false + + private let orientationChangeDuration: NSTimeInterval = { + UIDevice.currentDevice().userInterfaceIdiom == .Pad ? 0.4 : 0.3 + }() + public convenience init() { self.init(frame: UIScreen.mainScreen().bounds) } public override init(frame: CGRect) { + //self.textField = UITextField(frame: CGRect(x: -110.0, y: 0.0, width: 100.0, height: 50.0)) + super.init(frame: frame) + //self.addSubview(self.textField) + super.rootViewController = WindowRootViewController() } @@ -92,8 +112,10 @@ public class Window: UIWindow { set(value) { let sizeUpdated = super.frame.size != value.size super.frame = value + if sizeUpdated { - self.viewController?.setViewSize(value.size, insets: UIEdgeInsets(), duration: self.isRotating() ? 0.3 : 0.0) + self.updateViewSizeOnLayout = (true, self.isRotating() ? self.orientationChangeDuration : 0.0) + self.setNeedsLayout() } } } @@ -105,8 +127,10 @@ public class Window: UIWindow { set(value) { let sizeUpdated = super.bounds.size != value.size super.bounds = value + if sizeUpdated { - self.viewController?.setViewSize(value.size, insets: UIEdgeInsets(), duration: self.isRotating() ? 0.3 : 0.0) + self.updateViewSizeOnLayout = (true, self.isRotating() ? self.orientationChangeDuration : 0.0) + self.setNeedsLayout() } } } @@ -120,15 +144,39 @@ public class Window: UIWindow { self._rootViewController?.view.removeFromSuperview() self._rootViewController = value self._rootViewController?.view.frame = self.bounds - /*if let reactiveController = self._rootViewController as? ReactiveViewController { - reactiveController.displayNode.frame = CGRect(x: 0.0, y: 0.0, width: self.frame.size.width, height: self.frame.size.height) - self.addSubview(reactiveController.displayNode.view) + self._rootViewController?.setParentLayout(ViewControllerLayout(size: self.bounds.size, insets: UIEdgeInsets(), inputViewHeight: 0.0), duration: 0.0, curve: 0) + + if let view = self._rootViewController?.view { + self.addSubview(view) } - else {*/ - if let view = self._rootViewController?.view { - self.addSubview(view) - } - //} } } + + override public func layoutSubviews() { + super.layoutSubviews() + + if self.updateViewSizeOnLayout.0 { + self.updateViewSizeOnLayout.0 = false + + self._rootViewController?.setParentLayout(ViewControllerLayout(size: self.bounds.size, insets: UIEdgeInsets(), inputViewHeight: 0.0), duration: updateViewSizeOnLayout.1, curve: 0) + } + } + + var postUpdateToInterfaceOrientationBlocks: [Void -> Void] = [] + + override public func _updateToInterfaceOrientation(arg1: Int32, duration arg2: Double, force arg3: Bool) { + self.isUpdatingOrientationLayout = true + super._updateToInterfaceOrientation(arg1, duration: arg2, force: arg3) + self.isUpdatingOrientationLayout = false + + let blocks = self.postUpdateToInterfaceOrientationBlocks + self.postUpdateToInterfaceOrientationBlocks = [] + for f in blocks { + f() + } + } + + public func addPostUpdateToInterfaceOrientationBlock(f: Void -> Void) { + postUpdateToInterfaceOrientationBlocks.append(f) + } }