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.
Related
In my app, I have a screen that needs privacy, so when you first open it, it will check if you have logged in with the password. If not, it adds another view on top of the SuperView until that password is entered. I use XIB file for this. The main View of the screen is a tableview, and I drug on another UIView to use as the Password screen. However, despite AutoLayout, the Password view is only taking up the dimensions of 4.7" phone, meaning you can see behind it on the edges. I made sure AutoLayout is enabled, and on the background image, which is the gradient, I added 4 constraints, one for each side, set to 0, so that it would always cover the view behind it, and then set the UILabel to be aligned horizontally in container. What am I doing wrong? This works PERFECTLY in a different view and XIB within the same app, and I even copied the view straight over, but it is still messing up on this one class.
Code to load this is (password is the UIView declared in the header as IBOutlet UIView *password:
[self.navigationController.view addSubview:password];
The correct solution is to use a view controller to manage the curtain view, and either push that view controller onto the navigation controller's stack, or present it from the navigation controller or the tab bar controller.
There may be 2 possible reason
you may not applied constraint on your xib.
you have applied constraint on it but when you are initialising your xib you are providing fix frame.
it would better if your share your code how you are doing it ?
I designed about 40 view controllers using a 5.5 inch storyboard layout. After all of that I tested it on the iPhone 4S...big mistake. everything is jumbled together being for a larger screen size. I was able to fix one view controller up using Size Classes. I am wondering if there is any way I can adjust all 40 at the same time, or at least avoid doing this for every single one. It is really frustrating finding this out now. Thanks!
This is a relatively complicated issue you are attempting to solve, but I have two potential solutions. Both suggestions are based on moving your current interface into containing UIScrollView instances
If you are using storyboards, then for each of your view controller scenes, put a UIScrollView as a descendent of the view controller's view. From there, provided your subviews are contained within other views (like a container view for a set of buttons), you can move those into your scroll view. You will have to setup constraints to define the size of the scroll view's content, but this will allow the size of the device to have a smaller impact on the interface as you will get scrolling as needed.
If you are using nib files (.xib) then it is essentially the same thing, but easier. In this case, move a UIScrollView onto the canvas, but not as a subview of the default view. Once that is out there, move the original view to be a subview of the scroll view and set constraints to be 0 from the subview to the scroll view. Finally, right click drag from the File's Owner icon to the scroll view and set that as the view outlet.
Hopefully one of these will help you.
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.
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).
I have a UIScrollView which contains a view controller which contains a couple of contorllers - text fields, buttons, an image, etc...
When it is shown on portrait mode, I want it to act as if there is no scroll view ( its still there, of course, but disabled scrolling and bouncing and etc, content size is application frame's size ), and the custom view is just displayed normally, but when switching to landscape I want to make the scroll view available to use in order to avoid tight user interface.
I want to use a specific xib file for portrait/landscape modes and not place the objects differently using code, because of localization reasons
I encountered 3 problems doing this:
1)I cant find a way to load and apply to the controller a different xib when rotating ( I'd like it to be a smooth transition as well, but not as important right now )
2)I can't find a way to set up something beyond the application frame's in a xib file ( I dont have the buttons and etc directly in a scroll view, but inside a custom view which is inside a scroll view ), So I cant place anything on the invisible part of the scroll view ( the part that needs to be scrolled into in order to be seen )
3)I cant find a way to specify a scroll view's content size via a xib file, which is needed in order for the interface to be as flexible as possible for localization.
Do you have any ideas on how to implement/solve these needs?
Thanks in advance!
I would advise doing the first thing you said you wouldn't do:
I want to use a specific xib file for portrait/landscape modes and not
place the objects differently using code
I have spend a good amount of time trying to use a different XIB for different orientation but here is the deal:
1) If you have an UITextField in Portrait, when you rotate to landscape, you have to be sure that the text in portrait mode is the same in landscape, for example. You have to do that for every object.
2) Even if you dont look to point 1) I was never able to implement the two xibs solution successfully. I am not saying you cant do it, I am just saying from my experience, that I could not achieve that.
So, if you at least think doing the 1 xib solution, I could explain a bit:
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)orientation{
}
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
NSLog(#"My view will rotate");
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
[self viewsPositionPortrait];
} else if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)){
[self viewsPositionLandScape];
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
NSLog(#"Register should rotate");
return YES;
}
-(void)viewsPositionPortrait{
//Create the frame for your "oneOfmyViews" for the portrait rotation
oneOfMyviews.frame=CGRectMake(...,....,...,...);
}
-(void)buttonsPositionLandScape{
//Create the frame for your "oneOfmyViews" for the landscape rotation
oneOfMyviews.frame=CGRectMake(...,....,...,...);
}
You could then in the viewsPosition methods define your view's frames. What you win?
1) Clean code.
2) Beautiful transitions.
3) Specify your scroll view's content size easily.
For you problem with the rotation of your UIViews => CustomViewController => UIScrollView.
You can do something like this:
1) Do exactly the same thing in your custom view (methods above) for your UIViews inside your CustomViewController.
2) In the view that has your CustomViewControllers you can do the following:
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
//(everything that i posted above)
[myCustomViewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
This way you will make the UIViews inside your customViewController rotate. One last tip: disable autoresize-subviews and take out every auto-sizes from your UIViews (because you will be defining their frame individually).