I built a keyboard for iOS 8 using one xib file and multiple subviews. However this keyboard only works in portrait mode and when the device rotates, the keyboard doesn't change its size or length. I figured I need to create another xib file with subviews for landscape and when the device rotates have the xib files switch. My question is how would I go about accomplishing this? How would I make it so that the program knows when to switch xib files. I was thinking I should use something like what is proposed in here: https://stackoverflow.com/a/25222353/2057171 but I do not know how I would implement it. Any help is appreciated.
One xib for both portrait and landscape
It's possible to have only one xib for both portrait and landscape, if you enable autolayout on your views in your xib file. These are the steps I did in my own test keyboard.
Enable and setup autolayout on the views in the xib file
Load and add that subview to the "inputView"
Programmatically setup the constraints for this subview in relation to inputView
"inputView" automatically changes sizes when switching between portrait and landscape, so when you have constraints setup for your view in relation to "inputView", your view will adjust automatically.
Separate xib for portrait and landscape
If you do decide to have separate xib files for your portrait and landscape views, you might need to load and add the correct subview to inputView whenever the orientation changes. I haven't done this personally but I believe updateViewConstraints() is called whenever the orientation changes so you can implement your logic there.
Related
I suppose this is a more basic question. I am stuck with this app I am coding for the iPhone X. I have it set to display a lot of buttons on the Main.storyboard file. Everything looks how I want to look but the problem is I do not know how to make the screen change with landscape rotation left and landscape rotation right. The screens looks the same but I want to style the page differently.
Xcode 9.1
iOS 11
Swift 4.03
If you want a an actually different layout for landscape (and not simply an adaptive one) you should use size classes:
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/Size-ClassSpecificLayout.html
This will enable you to make completely different UI for different orientations.
Interface Builder lets you customize many of your layout’s features based on the current size class. The layout then automatically adapts as the size class changes.
...
As the view’s size class changes (for example, when you rotate an iPhone or switch an iPad app between full-screen and Split View), the system automatically adds items to or removes them from the view hierarchy. The system also animates any changes to the view’s layout.
If the only reason you want different layouts in landscape right and landscape left is to avoid the “notch” at the top of the screen and the gesture area at the bottom, then what you really should be doing is laying out your views relative to the “safe area”, which exists for just this reason. If your views are laid out relative to the safe area layout guides, then they’ll automatically adjust to avoid the notch and the gesture area automatically, regardless of orientation.
https://developer.apple.com/documentation/uikit/uiview/positioning_content_relative_to_the_safe_area
You could make two xib files for each specific orientation and then using what user dfd suggested, load the xibs respectively in the following code:
if UIDevice.current.orientation == UIDeviceOrientation.landscapeLeft {
//Load xib for landscapeLeft
} else if UIDevice.current.orientation == UIDeviceOrientation.landscapeRight
//Load xib for landscapeRight
{
This would allow you to style the orientations differently.
Good Luck,
Arnav
Source:
https://stackoverflow.com/a/38629833/8503080
I've to develop an iPad app that should have a layout for portrait and a layout for landscape. This app should be able to work with iOS 8 and iOS 9. To design two different layout should I use 2 storyboards: one for the portrait layout and one for landscape layout or I can use only one storyboard with size classes?
I found this solution over stackoverflow, but I'm not sure if it's the right way to work with 2 different layout. Does anyone have an idea about how to work with a portrait layout and with a landscape layout with iPad? So you think it's better to use size classes or to use 2 different storyboard: one for landscape and one for portrait
As per your question you need to use size class for creating application. But in traitCollection you will only get wRegular-hRegular for both landscape and portrait.
result of log value of trait collection with statusBarOrientation.
All you can do is add make constraints active and inactive in
-(void)updateViewConstraints {} method based on [UIApplication sharedApplication].statusBarOrientation.
First you need to bind the constraints you have given to the view and make their objects in .h or .m file.
Then you need to change the constant value of the constraint or make constraints active and inactive based on your requirement.
Result:
1.) iPad Portrait View
2.) iPad Landscape View
This is one way by which you can layout in iPad. Hope it helps you solving your problem.
I am trying to wrap my head around the new size classes in iOS8 and XCode6. I am attempting to create a nib, without a storyboard, and do something really simple--center a UIView on the screen.
Starting with a nib in the default size: w:Regular h:Regular, I place a 200x200 UIView onto the parent view and center it, then add contraints to pin it. In my preview pane, the UIView only shows up on the iPad view.
I need for this view to show up on iPads in lanscape and iPhones in portrait. I suppose some day i will understand why Apple thinks this is so much easier, but at the moment I'm bewildered.
When I switch my design view to any of the iPhone supported modes, the UIView disappears.
Even when I unpin the UIView and move it around in the design view, it never shows up in the iPhone view. In other words, I've tried everything to get this view to appear on an iPhone and nothing works.
Here is my UIView and settings:
Obviously there is something I'm missing, but I'm getting really frustrated trying to figure it out. Can anyone offer a clue? Thanks!
It's because you defined your view only for the size classes Regular & Regular. For them to show up on the iPhone you will have to configure the view also for the appropriate size classes, which in case of the iPhone is Regular & Compact where (<height> & <width>).
UPDATE:
The views generally only show up in the size classes for which they are configured. You currently have Regular & Regular selected, this can be seen from your screenshot. This means you defined it only for the devices with size classes Regular & Regular, which is the iPad in both orientations.
You need to add the views also in the size class that you are targeting, which are the size classes of the iPhone.
So I followed the following tutorial on creating custom views associated with device orientations:
http://www.theappcodeblog.com/2011/03/30/orientation-change-tutorial-change-the-view-when-the-orientation-changes/
To summarize the article, the author is able to generate custom views for landscape and portrait orientations by pointing to a new top level view in an XIB file upon detecting a device rotation.
Therefore, I took a complicated view hierarchy (many buttons, sliders, views within views), duplicated everything at the top level to create the landscape view. Then created custom arrangements for the landscape view. In a method where my code detects a landscape orientation, my code points to the top of the landscape hierarchy and portrait when detecting portrait.
This works, except all the IBOutlets (UIView, UIButton, etc) in landscape hierarchy are still referencing their equivalent objects in portrait view hierarchy. I do a lot of special processing on these UI elements during the run time so I require IBOutlets and not just IBActions. I found that my copied UI elements in the landscape view can point to the same IBActions, but they can not share IBOutlets with the ones in the portrait hierarchy.
Do I have to duplicate all the objects and have my code manage everything (figure out which objects to use)?
Here's one approach that may suit you.
If you load the landscape nib right at the time when you need to swap in the landscape view, you can pass your view controller as the nib file's owner, and the nib loader will overwrite your view controller's outlets based on the connections in the landscape nib.
And when you need to swap in the portrait view again, load the portrait nib and pass the view controller as the file's owner. The nib loader will again overwrite your view controller's outlets, this time based on the connections in the portrait nib.
You should lazily create a UINib object for each of the nibs the first time that nib is needed, and keep them around to speed up loading on subsequent rotations.
I solved it using loadNibNamed. Essentially what I had to do was save the state of all the IBOulet variables (using non IB variables) and other variables that are initialized in the view. When a new Nib is loaded, everything resets back to their default states. It's quite annoying, but this seems to be the best way to approach this for now.
I'm making an app that only supports landscape mode of an iOS device. So I have set it to only return YES for UIInterfaceOrientationLandscapeRight and UIInterfaceOrientationLandscapeLeft in shouldAutorotateToInterfaceOrientation: and set the initial orientation in Info.plist to UIInterfaceOrientationLandscapeRight. The app is displaying fine.
The problem is that the coordinate system is not in landscape until the view is finished loading (I'm not particularly sure here when it is being applied correctly). What I know is that it changes to the correct coordinate space when it calls the: shouldAutorotateToInterfaceOrientation upon launch. The problem is that when both viewDidLoad and initWithCoder: are called it is in the wrong coordinate space, before shouldAutorotate....
What I mean is that if I set a view to the full width of the screen with CGRectMake(0, 0, 480, 320) (instead of using self.view.frame or something) it is borked when the coordinate spaces are rotated "corrected". The views created in viewDidLoad have a rotation applied to them so they are wrong. This makes setting up views in those methods a pain and really illogical. I'm guessing that this is an issue with how orientations are handled. Why does it behave like this? I have told it in every way possible that I only want landscape position. Can this be something with Interface builder and properties there?
This is using a standard UIViewController. The new views are however also loaded from .xib files in viewDidLoad. Could this be the issue?
Support for orientation changes is something that all iOS developers struggle with at one time or another. It's often confusing and frustrating. Here's a rule of thumb which will handle 95% of all orientation issues:
Orientation related interface changes (such as frame resizing) don't happen until viewWillAppear:. Until then, your view will be in portrait mode even if you only support landscape in your app or your app is already in landscape mode.
Since viewDidLoad: occurs before viewWillAppear:, orientation layout changes haven't occured yet. So, doing any of the following in viewDidLoad will often have wonky results (depending on how the view's autoResize is set):
inserting a layer (such as a gradient) inside a view with a frame that is equal to the view's bounds. The layer inserted will always be the size of the view in portrait mode. If that view stretches when rotating to landscape view, your layer will be smaller than the view.
trying to determine the size of a table view and using that frame for some other view
manually adding a child view controller and using a container view's frame to determine the child view controller's view frame.
I was just reading over the UIViewController documentation today, and I remember reading about this.
From the “Handling View Rotations” overview section:
By default, the UIViewController class displays views in portrait mode only. To support additional orientations, you must override the shouldAutorotateToInterfaceOrientation: method and return YES for any orientations your subclass supports. If the autoresizing properties of your views are configured correctly, that may be all you have to do. However, the UIViewController class provides additional hooks for you to implement additional behaviors as needed.
…
Note: At launch time, applications should always set up their interface in a portrait orientation. After the application:didFinishLaunchingWithOptions: method returns, the application uses the view controller rotation mechanism described above to rotate the views to the appropriate orientation prior to showing the window.
http://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006926-CH3-SW57
One thing I can think of that might be causing you extra trouble is if you’re setting autoresizingMask options on your subviews. Could make predicting what will happen when the view is rotated and resized very difficult if all your numbers are wrong.
If you're using custom UIVIews, you might want to override layoutSubviews: to handle the different times when it may be necessary to layout the subviews again (or when you call setNeedsLayout).
You can reorder the Supported interface orientations in [project]-info.plist file, set item 0 value to Landscape (right home button).