I am trying to layout user interfaces differently for Portrait & Landscape orientations. The difference comes in the following ways:
I have few UIStackViews. Some of their axis becomes horizontal in landscape but vertical in portrait mode,
The order of buttons in UIStackView may be different in both the modes,
A button which is added to subview1 in landscape needs to be removed & added to subview2 in landscape. subview1 may not be present at all in portrait mode,
Autolayout constraints are different in both the modes.
I started with vary for traits in XCode but it seems to have limitations. I am manually switching constraints in the code on observing trait collection changes (viewWillTransition:...) but it seems clumsy. Is there a better way or the best way would be to have duplicate sets of controls for both the modes and hide the ones not needed in landscape/portrait modes?
The interface builder is really powerful if you use it right, I'm not completely sure what you're trying to do, it'd be useful if you share some screenshots at least. I'm assuming that you're every subview of the UIStackView is a button, or it contains a button, in that case what I'd do is:
Having 2 different UIStackView, one for landscape and the other for portrait.
Add variants to the installed interface builder property for traits. So only one of the two UIStackView will be installed at a time.
For every UIButton add the event, connect it to the respective IBAction or selector. This will work even if there're 2 buttons that do the same.
So regarding your issues:
The axis itself wont change, but this will work because there're 2 version of the UIStackView.
You can choose to arrange the button on each UIStackView.
Just add the buttons wherever you want on each UIStackView.
Set the constraints as you want depending on which UIStackView you're working on.
An I missing something? please update your question with more information so people can help you in a better way.
What I have done is to observe the UIDevice.orientationDidChangeNotification and update the necessarily constraints to achieve different layouts, as you have already implemented. And I think this is the better way to solve the problem and never trying or even thinking to create different views for each orientation 🙅🏿‍♂️.
Attending the conditions:
The first condition only needs to set the axis property accordingly.
The order of the arrangedSubviews is part of the design, and therefore, its up to the developer to rearranged them manually if he/she has to.
In this part what I would do is what you have said: to removeFromSuperview() the button and addSubview(view:) from one subview to another.
This is made automatically, as long as we activate the constraints for one orientation and deactivate the other ones, the system update the UI without any problem.
The most important thing is to make sure that when removing subviews, any constraint attached to it is removed, or the app will crash.
Related
I used to be able to do this:
UIButton *bigBottomBtn=[[UIButton alloc]initWithFrame:CGRectMake(0, self.view.frame.size.height-60, self.view.frame.size.width, 60)];
I also used to be able to just drag a button onto a storyboard and add a constraint that would hold it to the bottom of the parent.
What is going on with Xcode, Autolayout and Apple for that matter....is my Xcode not working properly? Have I missed a major memo? is Apple just going downhill fast?
Your button-creating code used to work (and still does) if self.view's frame was correct at the time you created the button. Note that the view doesn't necessarily come out of the xib or storyboard with the correct frame; the xib/storyboard contains the view at some design size which might not match the current device. This wasn't as much of a problem when all iPhones had 3.5 inch screens, but became a pretty common problem with the advent of the iPhone 5's 4 inch screen.
The view isn't guaranteed to have its correct frame until its superview's layoutSubviews returns, so if for example you're creating bigBottomBtn in viewDidLoad, that's too early. Many questions on stackoverflow cover this problem. You either need to set the autoresizingMask of the button, or implement layoutSubviews or viewDidLayoutSubviews to update the button's frame, or turn off translatesAutoresizingMaskIntoConstraints and install constraints. Note too that your view can change size if you support rotation, slide over or split view multitasking, or if your view can be the detail view of a UISplitViewController, so it's a bad idea to try to guess the correct frame of the button based on the device's screen size at the moment the button is created.
Note that storyboards now by default use a design size of 600x600, which isn't the size of any device. This is probably because if Apple chose some device's size (say, the iPhone 5's 320x568) as the default, and you happened to use a device of that size as your primary (or only) test device, you could easily forget to think about what your app will look like at other sizes. However, you can explicitly set the design size to some device's size if you want:
I usually use “iPhone 3.5-inch” if I don't specifically need something bigger, because it lets me get the most scenes on the screen simultaneously (and produces the smallest screen shots for stackoverflow).
As for “I also used to be able to just drag a button onto a storyboard and add a constraint that would hold it to the bottom of the parent”, I have good news: you still can. Example:
However, you do need to be careful if you have filled your root view with a table view as appears to be the case in your screen shots. You need to drag the button to the document outline in that case, because if you drop it on the table view, Xcode will assume you want it to be the table view header:
Trying to pin a table view header to the bottom of the screen would be folly.
As for the Editor > Align menu, I have found that the items can be mysteriously inactive, which is frustrating.
Note, though, that only the “Horizontally in Container” and “Vertically in Container” will work (when they work at all) with a single view selected. To use the other items in the menu, you need to have at least two views selected, because the other items align the selected views with each other by setting their frames:
If you only have one view selected, Xcode doesn't know what other view you might want to align it to.
Those menu items are perhaps useful in the springs'n'struts model, but they don't add constraints, and under autolayout you probably want constraints to enforce the alignment at run time.
As far as I know, those menu items have never added constraints, but I'm not going to reinstall Xcode 6 to verify that, because there's a convenient popover that will add constraints corresponding to all of those menu items:
In xcode you always need to add buttons according to its visibility. As you said you need to show button on top of tableView and it should be aligned to bottom. For that You just need to arrange the order of items. as shown in the image below.Provide the layout for the button.
I've been doing iOS for a while now, but when it comes to dynamically hiding / showing elements, I'm a bit lost.
Coming from Android, I'm used to being able to simply set views to visibility gone, but this doesn't exist on iOS.
So let's say I have the following scenario:
Basically I want to have a table, but the table should not fill the entire view controller. Instead it should leave place for optionally either a button, a multiline label, or possibly both at the bottom (if visible, these should be fixed, not scroll).
One way to solve this would be to use auto layout and modify constraints, like adding a zero height constraint. But that would make iOS kill one of the other constraints, which would make it hard to change it again. For the label, I wouldn't always want to have a height constraint, because it could be multiline, and should take the size it needs.
Maybe it's easier to skip autolayout here and modify frames instead, I don't know.
My question is: What approach would be best here?
Is there some other way of doing this I haven't thought of, or do I have to try to do what I described above?
I'm not primarily looking for code (code can be ok), but I'm more interested in a description of how it can be done.
I'd like to support iOS 7.
This problem had a variety of solutions, and opinion based, but I'm facing such questions a lot, when I don't know what to choose and what would be the "right thing".
So, I my opinion, the best solution here is using autolayout, you need to set height of label manually, but you have a few methods for this, at least you can play with it and if you don't succeed ask question about it. Using frames, you'll face same problem of calculating height, right? But with auto layout, you only need to set height, vertical space to 0, when you need to hide message.
You can also use constrains with priority lower 1000, and remove completely constraints from message (button, label) if you don't need it at all anymore.
For example, taking your layout image, you can make UIView with subviews: button, label. Top constraint connect to the UITableView, other constraints to the sides.Label and button will calculate the view's height. The only question here is label height.
So in ios assuming that the background of both these objects is opaque only the front most view in the Heirarchy will be visible and interactable, An easy solution would be to change the different frames of these two things you need and make sure they are in the back of your view heirachy, and when you need them to appear use view.bringSubviewToFront(mySubview) and view.pushSubviewToBack(mySubview) to make it disappear again. View obviously would be referring to main view of your view controller.
I am starting a new project which should be working on every iOS device size possible.
The project is rather simple. The main view will be a scroll view and it will hold a '+' button (where it says 'button' in the image) in the top right corner. (It does not really matter, I'm just trying to give the general idea of what I'm trying to understand and implement.)
A small example:
What is the right approach for this kind of problem?
Should I create a different storyboard for each device?
Should I start creating an adjustable scroll view that will hold the needed buttons with some constraints (if at all possible)?
I have read this tutorial:
auto layout
which explains the auto layout nicely, but does not mention the issue I'm trying to figure out.
Any thoughts?
Use AutoLayout and position your views relative to one another (so no x pixels spacing between views). Make only one storyboard for all devices, when a different view should be loaded on another device (like a completely different layout), select the appropriate size class and adapt the views and constraints.
Then it will be recalculated on every device.
The storyboard has a preview section where you can choose a device to simulate the view on.
I have issue with autolayout.
I have three different views (could be n views as well). When I pin first one to top of super view and connect one next to the other everything seems fine. But if I want to change height of first one the other two does not follow (they stay on the same position). What I find weird about this is that if I connect only two views this works perfectly.
To have better understanding what I am trying to achieve I made simple, example solution with three views:
http://i.stack.imgur.com/cMwiB.png
So, when I set height of green view to 0 ( can be any other number as well ) and only red view is connected, this works fine, red will move as it should. Blue one is not connected and it remains where it is:
Here is screenshot when I connect blue to red:
What I tried to do:
connect blue one to green as well ( not working)
connect blue only to green ( not working )
"boxing": have boxes with views at the time ( ugly, hard to manage, not working in all cases)
What I would like to know:
what makes this happen? Can I change this kind of behavior?
What is the best practice to have flexible length for height (Besides UITableView)
Thanks in advance.
Ok, I think you're falling into the same trap I did, when I investigated autolayout some time ago. It's tricky to explain, but I'll do my best. When you work with autolayouts, you need to forget completely about setting a view's frame at all. And that, my friend, includes changing size and positioning. When you use autolayout, you define some constraints, some "rules", that the view tries to respect when rendering the screen, so the more specific the rules are, the less random will be the behavior of the view. I mean, if you just define the spacing between views, you're implying that the height of the views doesn't matter, so it's possible that some views grow or shrink when the re-layout is called (that is, if you don't specify a constraint for size. You probably want to always specify the size of some views in every layout...)
By the way, you're allowed to violate the constraints by manually changing the frame of an element AFTER a re-layout is called, but when the re-layout method is called again, the constraints will be forced so the size and positioning of the views will change. Quick tip: an easy way to force the re-layout method is to change orientation (command+left / command+right in the simulator).
So after saying that, I have to say that the layout you provided is completely working as intended, at least to me. When you change orientations the constraints you specified are ALWAYS being respected. If you want, you can try to apply some frame-setting in the viewDidAppear method, because this one is called AFTER the autolayout (and thus, you're able to violate the constraints temporally). After doing this, your view will be broken but once you change orientation the constraints should be respected again. Make sure it's that way.
From here I can only wish you luck ;) Oh, and refer to the documentation, it was a life saver to me when I looked at this half a year ago. https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/Introduction/Introduction.html It might seem a classic, but it's a pretty nice doc.
EDIT: one last thing. I have the impression that constraints are not designed to be dynamic. You define them once, and they're there always. Their main purpose is to ensure that subviews are rendered as intended in every screen resolution without fail, so if I were to make an application with moving views, I would leave them outside of the autolayout, or avoid using it at all. Just a personal impression though :)
I am working on an iPad app and want to support both portait and landscape orientation.
Currently, I am seeing three ways to handle orientation:
1) Apple recommends using two view controllers (http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/RespondingtoDeviceOrientationChanges/RespondingtoDeviceOrientationChanges.html)
2) The Beginning iPhone Dev book I have uses a single xib file with multiple UIView (one for portrait, one for landscape), swapping the view in the willAnimateRotationToInterfaceOrientation() method.
3) Others have said the "proper" way to do it was using layoutSubviews and autosizing mask.
Before, when I build an app, I would write my entire app purely in code, including interface setup and layout. The result was I ended up with a massive and very painful to tweak interface code.
Now after writing a number of apps in purely code, I kind of appreciate why there is Interface Builder - to help manage the interface and more so, a view controller should ideally only be for managing data and sending it to the views, not managing how the views should look.
Using Interface Builder, it makes sense to have two separate custom UIViews, one for portrait and one for landscape but I've been told otherwise that layoutSubview is the better option.
But if I were to use layoutSubview, that would mean I have to go back to writing and managing my interface using code, which doesn't seem right to me.
Is using layoutSubviews() really the way to go ?
I really don't see any benefit it has over other method. Some say layoutSubviews allow you to animate the transition to the other orientation.
My two interface isn't a simple resize button or text width when the device rotates. I have adjacent sliders next to text fields that should be stacked vertically when rotating to landscape and other sliders that needs to be moved to the other side of the screen etc.
edit
might be a duplicate questions of:
Handle iPhone orientation Landscape/Portrait
iPad/iPhone multiple orientations best practice?
Moderators can close if need to.