Xcode traits views not nil - ios

As known different views can be added for each screen unit class(Regular, Compact) By picking the desired unit classing then clicking the Vary for Traits button.
Then, connect the views to your controller twice using outlets. one for general and one for that unit class.
In viewDidLoad I printed the values of both views and noticed that both
of them are not nil.
Shouldn't I have one of the views equal to nil since this is not its unit class? How does view traits realy work?

All views (and constraints) are instantiated and referenced with their corresponding outlets when your storyboard scene (or nib) is loaded — no matter what size class they are installed on.
From Apple's Auto Layout Guide:
When the system loads a scene, it instantiates all the views, controls, and constraints, and assigns these items to the appropriate outlet in the view controller (if any). You can access any of these items through their outlets, regardless of the scene’s current size class. However, the system adds these items to the view hierarchy only if they are installed for the current size class.
This is because your view's size class does not only vary depending on the device, it may also change when you rotate your device or do multitasking on an iPad. When you change your layout as a response to a user action (like a tap) you really wanna make sure that you update the layouts for all your size classes.
Example:
Say you have a simple "register view" with a username text field where users can claim their unique username. You want to give the user immediate visual feedback if the username he typed in is already taken or not.
You decide that on a compact size class (iPhone in portrait mode) there is just enough space to show a litte red cross (❌) on the right edge of the text field. On the iPhone 6/7 Plus in landscape mode however, there is enough space to show the user a little more information and so you choose to show a label "Username taken, please try another one!" next to the red cross on the regular size class.
Now every time the user has entered a new character in the text field you can validate the input and then update the UI like this:
func updateUI(isUsernameAvailable: Bool) {
redCross.hidden = isUsernameAvailable
usernameTakenLabel.hidden = isUsernameAvailable
}
At this point you can be sure that the UI is up-to-date no matter how often the user rotates the devices. If the usernameTakenLabel was not instantiated here you would have to somehow remember that the label should be hidden/visible once it shows up with an extra instance variable and then use that variable to actually hide/reveal the label once it is instantiated i.e. when the size class width changes which bloats your view controller and is pretty error prone.

Related

How do I make the elements in my UIView responsive?

My goal is to create an alert that has three text fields, one taller than the others, and an image that, when tapped, allows the user to choose a picture to replace a set default one.
After unsuccessfully searching for a library for this, I decided to create my own alert by placing a UIView off the screen and, when prompted by a button, would zoom onto the screen; it consists of all the elements I require.
When I run the application, the view pops up correctly, but none of the elements on the view are responding to touch. I've checked that isUserInteractionEnabled for everything is turned on.
What's also odd is that when I keep the view on the screen (instead of placing it some distance away on Storyboard), all the elements work fine.
I'm assuming it had something to do with the animation. I tested it with a fade in instead of a displacement, and the result was the same - the elements were unresponsive.
In order for your elements to be responsive you have to link the action of you clicking them to your view's code. You can do this in a non-programmatic manner by ctrl-clicking your element on story-views and then dragging to the view controller. Then choose action instead of outlet, and choose when the action you want will be triggered (bottom part). Then insert your code in the viewController.
So I figured it out. I used the debug view hierarchy and saw that the alert was behind the elements behind it, even though it was still being shown (for some reason). I changed the zIndex of the UIView and it worked!

viewWillTransitionToSize: vs willTransitionToTraitCollection:

Can anyone explain the differences between these 2 methods? The docs for UIViewController explicitly state that viewWillTransitionToSize should be used for managing rotations, but clicking through to the UIContentContainer page, the willTransitionToTraitCollection method makes a confusing entrance.
I think I understand the conceptual difference between a size class change ( trait collection change ) and a size change, but I'm not sure which method to implement in which circumstances. Clarification from a UIKit wizard would be helpful!
Whenever you want to do something aa a response to a user rotating their device you should use viewWillTransitionToSize, if you do that you know for sure that your actions are executed as this is called every time your app's window changes size.
If you only want to take action when the trait collection changes, for instance if you have a certain collectionViewLayout set for a Compact size class and another you want to use for Regular you use willTransitionToTraitCollection.
If the trait collection changes then the size also changes. But it doesn't work the other way around. A portrait iPad and a landscape iPad have the same traits but are different sizes. Add multitasking to the mix and you have a whole variety of sizes that would map to just two traitCollection size classes.

Size class initially unknown

I am trying to setup my UI on my universal app. I have a storyboard setup with size classes, a fairly simple UI. I have my view controller with a view in. Inside this view, I draw a chart so this can only be updated using setFrame.
This is where my problem begins. I set my graph to be the screen width. However, when the view initially runs, the size class seems to be unknown.
As the default 'Any' size in my storyboard is 600x600. My view thinks it should draw 600 wide on my iPhone, which clearly isn't this wide.
It is only after I physically move the iPhone to toggle an orientation change, that it updates and recognises the correct size.
So my question is, how do I prevent this problem? I need my UI to know what size to be from the get go, not just after the user rotates their iPhone.
However, when the view initially runs, the size class seems to be unknown.
It is unknown to the view and the view controller, because at that time the view controller is not yet part of the interface and has no environment. But it is not unknown to UIScreen.mainScreen(). So if you need this information very early, that is who to ask.
However, as you've been advised in a comment, it also sounds like you may simply be doing this too early. Nothing in a view controller's view, including the view itself, has achieved its actual size until viewDidLoad or later.
There are two to prevent this problem
(1) Load your entire method in
-(void)viewDidAppear:(BOOL)animated
{
}
(2) Do the following step
Go to file Inspector
Uncheck "Use size classed

Using size classes programmatically

I (hopefully) watched all the relevant WWDC2014 session videos and read the docs, so this question is mostly to confirm my suspicions, but please educate me.
What I want to do is animate views using Auto Layout. That in itself is not a problem. But these animations' endpoints change with different orientations. I thought I might be able to use size classes to move the views automatically on rotation, but Apple's developer guide says that animations have to be done programmatically, and from what I can gather, size classes are an Interface-Builder-only thing.
Another idea I had was using custom layout guides like the top/bottom ones IB provides, but those seem to be hardcoded.
The last thing I could do is update constraints by hand after listening to rotation events, but that is nothing new, and I feel like size classes should be useable for more than just static interfaces. Am I overestimating their purpose?
TLDR: Given two points A and B that a view can have its origin at (due to animations), how can I move both points using size classes or something similar?
After some more digging in the docs I have finally found something useable. The UIContentContainer protocol defines willTransitionToTraitCollection(:withTransitionCoordinator:), and that method's first parameter (a UITraitCollection) contains horizontal and vertical size classes as well as a UIUserInterfaceIdiom (that can be used to know whether the app is running on a iPhone or iPad, although size classes should be used for most things).
Additionally, since iOS 8 hides the status bar in landscape view, traitCollectionDidChange(previousTraitCollection:) is the corresponding method that gets called after the change happened, so the value of UIApplication.sharedApplication().statusBarHidden has changed when this method is called. Can be useful for UIScrollView's contentInset for example.
Lastly, if you need the exact screen sizes (in points, of course, but the above mentioned trait collection also knows about pixel density), there is viewWillTransitionToSize(:withTransitionCoordinator:).
Hope this helps someone else as well.

Why are two different UIToolbars, defaulting to different styles?

Consider the following two toolbars that are in the same project of mine:
Notice that these toolbars look different? The problem is that they were both created by dragging and dropping them into IB, and I didn't change any of there associated properties. All that I did change was adding the flex controller, and change the text on the initial UIButtonBarItem. Other than that, these Toolbars have not been modified and, furthermore, I've verified that their properties are exactly the same in the Attribute inspector.
How or why are they different? Furthermore, how can I get the first, bluish UIToolbar to look like the grey one since the available Black Opaque and Black Translucent styles look nothing like the grey one?
Am I missing something? This doesn't make any since.
I've found why this is happening but I'm a bit clueless on how to change this. See this, new question regarding a fix for this issue.
These Toolbars are changed due to internal Xib settings that are specified when the Xib is created. In the first example, when I created the UIViewController I must have unchecked the Target for iPad check box, even this is for an iPad project.
This changed the default size of the initial UIView that was in the Nib. I always cut off the statusbar and set the view to be freeform in sizing as soon as I create a UIView within a Xib file, so I can never tell by looking at my views whether they were targeted for the iPad or not.
When I created the second UIViewController object, I checked the Targeted for iPad option. Now, my toolbar is styled with a grey tone.
The lesson learned is obvious-- if you want consistency in the default style of your objects that you pull from the IB Toolbox, be sure to create your UIViewControllers, specifically targeted for the iOS device. If you've made the same mistake that I have, follow the linked question above for how to revert your UIViewController and Xib file to the other style of View Controller.

Resources