{"id":3280,"date":"2015-03-28T10:42:06","date_gmt":"2015-03-28T16:42:06","guid":{"rendered":"http:\/\/benincosa.com\/?p=3280"},"modified":"2015-03-28T10:42:06","modified_gmt":"2015-03-28T16:42:06","slug":"subview-rotation-on-ios-8-with-swift-while-keeping-static-main-view","status":"publish","type":"post","link":"https:\/\/benincosa.com\/?p=3280","title":{"rendered":"Subview Rotation on iOS 8 with Swift while keeping static main view"},"content":{"rendered":"<p>That title is a mouthful. \u00a0Basically, I just want to know how to imitate the native iPhone camera app. \u00a0The camera button stays locked where the home screen is but the icons rotate with the device as well as the capture area. \u00a0Should be pretty simple right?<\/p>\n<p>tl;dr: \u00a0<a href=\"https:\/\/github.com\/vallard\/RotatingSubviews\">See the code here on github that I did.<\/a><\/p>\n<p style=\"text-align: center;\"><a href=\"http:\/\/benincosa.com\/wp-content\/uploads\/2015\/03\/rs.gif\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-3281\" src=\"http:\/\/benincosa.com\/wp-content\/uploads\/2015\/03\/rs.gif\" alt=\"rs\" width=\"600\" height=\"400\" \/><\/a><\/p>\n<p>Well with iOS 8 Apple introduced the concept of adaptive layouts. \u00a0<a href=\"https:\/\/developer.apple.com\/videos\/wwdc\/2014\/\">See WWDC session 216.<\/a>\u00a0 This introduced size classes and traits which I think is fantastic. \u00a0Except for when you want to imitate the iOS camera application. \u00a0Then you don&#8217;t know what to think.<\/p>\n<p>There were two main ideas I could have used that I came across:<\/p>\n<p>1. <a href=\"https:\/\/github.com\/jstn\/IndependentRotation\">\u00a0Using two windows<\/a>. \u00a0This was brilliant and I think would work. \u00a0This even came with sample code to show how to keep the rotations separate. \u00a0I had read other people saying it was a bad idea to have two UIWindows. \u00a0I played with this a little bit but it seemed too much for what I needed. \u00a0Plus, I had the UI Tab Bar controller as the root image so it was somewhat complicated.<\/p>\n<p>2. \u00a0<a href=\"https:\/\/developer.apple.com\/library\/ios\/featuredarticles\/ViewControllerPGforiPhoneOS\/RespondingtoDeviceOrientationChanges\/RespondingtoDeviceOrientationChanges.html\">UIInterfaceOrientation<\/a>. \u00a0These methods all seem to be depreciated in iOS8 and may or may not work. \u00a0The problem with these methods is that the root method gets the notification and then signals to every body else. \u00a0I may have been able to work with this but I didn&#8217;t want to go through all the way down the hierarchy and implement all these methods for those that should be static and those that shouldn&#8217;t be.<\/p>\n<p>I went with <a href=\"https:\/\/developer.apple.com\/library\/prerelease\/ios\/documentation\/EventHandling\/Conceptual\/EventHandlingiPhoneOS\/motion_event_basics\/motion_event_basics.html#\/\/apple_ref\/doc\/uid\/TP40009541-CH6-SW1\">UIDevice.orientation<\/a>.<\/p>\n<p>Here&#8217;s the steps:<\/p>\n<h3>1. Subclass UITabBarController<\/h3>\n<p>Since my main project has a tab bar as the root interface I started here. \u00a0This way I want all the views to be able to rotate and use auto layout to do this. \u00a0There&#8217;s just one subview that needs to stay the same. \u00a0This was accomplished by adding the following method to the view controller:<\/p>\n<pre class=\"lang:swift decode:true\">override func shouldAutorotate() -&gt; Bool {\r\n        if (self.selectedIndex == 0){\r\n            return false\r\n        }\r\n        return true\r\n}\r\n\r\n\r\n<\/pre>\n<p>This makes it so that this view 1 in the tab bar won&#8217;t rotate.<\/p>\n<h3>2. \u00a0Subscribe for notifications in the App Delegate<\/h3>\n<p>I may have been able to do this in the main class, but I did it in the app delegate in case it didn&#8217;t get alerts. \u00a0Then I had that propagate another notification. \u00a0This may be a redundant step, but figured I&#8217;d try it and was too lazy to change it back.<\/p>\n<pre class=\"lang:swift decode:true \">func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -&gt; Bool {\r\n        \/\/ Override point for customization after application launch.\r\n        \r\n        NSNotificationCenter.defaultCenter().addObserver(self, selector: \"orientationChanged:\", name: UIDeviceOrientationDidChangeNotification, object: nil)\r\n        \r\n        \r\n        return true\r\n}\r\n\r\nfunc orientationChanged(notification: NSNotification ){\r\n        \/\/println(\"Application received orentation change notification\")\r\n        UIDevice.currentDevice().beginGeneratingDeviceOrientationNotifications()\r\n        let orientation : UIDeviceOrientation = UIDevice.currentDevice().orientation\r\n        var num : NSNumber = NSNumber(integer: orientation.rawValue)\r\n        let userDict : [String: NSNumber] = [ \"orientation\" : num ]\r\n        NSNotificationCenter.defaultCenter().postNotificationName(\"orientationWillChange\", object: self, userInfo: userDict  )\r\n        UIDevice.currentDevice().endGeneratingDeviceOrientationNotifications()\r\n\r\n}<\/pre>\n<h3>\u00a03. \u00a0Subscribe to notifications in the non rotating view controller.<\/h3>\n<pre class=\"lang:swift decode:true \">override func viewWillAppear(animated: Bool) {\r\n        super.viewWillAppear(animated)\r\n        self.tabBarController?.tabBar.hidden = true\r\n        \/\/ Add observer to listen for device rotation\r\n        NSNotificationCenter.defaultCenter().addObserver(self, selector: \"orientationChanged:\", name: \"orientationWillChange\", object: nil)\r\n }<\/pre>\n<p>Now to react to these we&#8217;re going to rotate the subviews that need to be rotated. \u00a0This is done in 3 methods:<\/p>\n<pre class=\"lang:swift decode:true \">func orientationChanged(notification: NSNotification ){\r\n        println(\"Recieved notification of orientation change\")\r\n        UIDevice.currentDevice().orientation\r\n        if let info = notification.userInfo as? Dictionary&lt;String,NSNumber&gt; {\r\n            if let ori = info[\"orientation\"] {\r\n                println(\"orientation: \\(ori)\")\r\n                let newOr : UIDeviceOrientation = UIDeviceOrientation(rawValue: ori.integerValue)!\r\n                rotateSubviewsForOrientation(newOr)\r\n            }\r\n        }\r\n    }\r\n    \r\n    func rotateSubviewsForOrientation(orientation: UIDeviceOrientation) {\r\n        \/\/ rotate the subviews.\r\n        switch orientation {\r\n        case UIDeviceOrientation.LandscapeLeft:\r\n            \/\/ home buitton facing right\r\n            subLabelTransform(CGFloat(M_PI_2 ))\r\n        case UIDeviceOrientation.LandscapeRight:\r\n            \/\/ home button facing left\r\n            subLabelTransform(CGFloat(3 * M_PI_2))\r\n        default:\r\n            subLabelTransform(CGFloat(0))\r\n        }\r\n    }\r\n    \r\n    func subLabelTransform(f: CGFloat) {\r\n        UIView.animateWithDuration(0.2, animations: { () -&gt; Void in\r\n            \/\/\r\n            self.label1.transform = CGAffineTransformMakeRotation(f)\r\n            self.label2.transform = CGAffineTransformMakeRotation(f)\r\n            self.mainLabel.transform = CGAffineTransformMakeRotation(f)\r\n            \r\n        }) { (Bool) -&gt; Void in\r\n            \/\/println(\"Done\")\r\n        }\r\n        \r\n        \r\n    }<\/pre>\n<p>Maybe you have a better way? \u00a0I&#8217;d love to know!<\/p>\n<p>There is one problem with this method: \u00a0If the application launches in landscape mode then you&#8217;ll have to rotate it a few times to actually work in the right mode.<\/p>\n<p><a href=\"https:\/\/github.com\/vallard\/RotatingSubviews\">See the full code here<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>That title is a mouthful. \u00a0Basically, I just want to know how to imitate the native iPhone camera app. \u00a0The camera button stays locked where the home screen is but the icons rotate with the device as well as the capture area. \u00a0Should be pretty simple right? tl;dr: \u00a0See the code here on github that&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[995,28,36],"tags":[727,724,726,725,728],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3280"}],"collection":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3280"}],"version-history":[{"count":1,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3280\/revisions"}],"predecessor-version":[{"id":3282,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3280\/revisions\/3282"}],"wp:attachment":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3280"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3280"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3280"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}