iOS 8 - UIWindow Orientation Is Always Portrait - ios

I am updating an iOS 6 app to iOS 8 (iPad), and any new UIWindows that I create are always showing up in Portrait mode. The app originally supported both the Portrait and Landscape orientations but now will only support Landscape.
I've changed the supported orientations in the project file to Landscape Left and Landscape Right. The entire UI shows up in landscape, as expected, but when I create a new UIWindow, it shows up in portrait. The new UIWindow's frame matches the screen's frame exactly, so I can't imagine why/how it is showing up in portrait mode.
The following code is what I am using to create and show the new UIWindow, which acts as a modal:
var modalWindow:UIWindow = UIWindow(frame: self.view.window!.bounds)
modalWindow.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.66)
modalWindow.hidden = false
modalWindow.windowLevel = (UIWindowLevelStatusBar + 1)
modalWindow.addSubview(customView)
modalWindow.makeKeyAndVisible()
I've been struggling with this for a few hours; shouldn't the UIWindow be in landscape mode by default since the app only supports the Landscape orientation?
I would greatly appreciate any advice on how to resolve this issue.
EDIT:
I just created a new test app that only supports Landscape Left and Landscape Right, and the issue occurs there as well. Is this a bug? I can't seem to understand why the UIWindow would think the app is in Portrait mode when it's in Landscape mode.

EDIT (July 2, 2015)
This answer breaks in iOS 8.3+ and possibly previous versions of iOS 8. I DO NOT recommend that anyone uses it, especially since it is not guaranteed to work in future iOS versions.
My new solution uses the standard presentViewController method of a UIViewController. I initialize a UIViewController, add my custom modal View as a subview of the UIViewController, and then position the modal View using constraints. Works like a charm!
Original Answer
I left this for a while but decided to come back to it today. I ended up rotating the modal window -90 degrees since it is automatically rotated 90 degrees to be displayed in portrait. I also added a small calculation for the modal window's width and height to support both iOS 7 and iOS 8. Here is my new code:
// Get the screen size
var screenSize:CGSize = UIScreen.mainScreen().bounds.size
// Use the larger value of the width and the height since the width should be larger in Landscape
// This is needed to support iOS 7 since iOS 8 changed how the screen bounds are calculated
var widthToUse:CGFloat = (screenSize.width > screenSize.height) ? screenSize.width : screenSize.height
// Use the remaining value (width or height) as the height
var heightToUse:CGFloat = (widthToUse == screenSize.width ? screenSize.height : screenSize.width)
// Create a new modal window, which will take up the entire space of the screen
var modalWindow:UIWindow = UIWindow(frame: CGRect(x: 0, y: 0, width: widthToUse, height: heightToUse))
modalWindow.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.66)
modalWindow.hidden = false
modalWindow.windowLevel = (UIWindowLevelStatusBar + 1)
modalWindow.addSubview(customView)
// Create a -90 degree transform since the modal is always displayed in portrait for some reason
var newTransform:CGAffineTransform = CGAffineTransformMakeRotation(CGFloat(-M_PI / 2))
// Set the transform on the modal window
modalWindow.transform = newTransform
// Set the X and Y position of the modal window to 0
modalWindow.frame.origin.x = 0
modalWindow.frame.origin.y = 0
modalWindow.makeKeyAndVisible()
As stated, this works great on both iOS 7+ and iOS 8+! When working with subviews, note that the modal's width and height values are switched since it's displaying in portrait. So, if you want to center a subview horizontally, use the modal window's height and the subview's width instead of both views' width.

Related

Swift button frame height issue (viewDidLayoutSubviews)

I've got some square buttons that I'd like to add rounded corners to that are proportional to the button's height. In past versions of my app, I had implemented this feature without issues using viewDidLayoutSubviews(). For some reason, after pushing a new version of my app with other features I had tweaked, this section of code no longer functions as expected. Here is the code:
override func viewDidLayoutSubviews() {
for button in buttons {
button!.layer.shadowColor = UIColor.black.cgColor
button!.layer.shadowOffset = CGSize(width: 0, height: 1.0)
button!.layer.shadowOpacity = 0.4
button!.layer.shadowRadius = button!.frame.height / 40
button!.layer.cornerRadius = button!.frame.height / 10
}
Again, this block of code used to work just fine but for some reason it no longer works. What I am experiencing is much larger relative radii on smaller buttons (iPhone SE) compared to bigger buttons (iPads).
To troubleshoot, in viewDidLayoutSubviews(), I'm printing the button!.frame.height and I'm noticing that no matter what device I use the frame height is 395.5, which I believe is the correct size only on the 12.9" iPad. Therefore, the buttons look correct on the 12.9" iPad but the radii end up being too large on all of the smaller devices.
Any idea what's going on here? Why is it that they're all returning the same frame height even though they're visually very different sizes on the different devices?
I copy and pasted the above code into the viewWillAppear() method and
the problem was resolved. I then deleted the code from
viewWillAppear(), leaving me with my original code during posting of
question, and it is continuing to run as expected (working). What
could possibly be the cause of this intermittent behavior
The reason when you initialized the buttons in viewWillAppear and remove them but it still work because your button's frame did not change in the viewDidLayoutSubview method. And the viewDidLayoutSubview is invoked only controller's view is updated, rotated, or changed, which in your case it does not.
If you try to rotate your device you will see your parent view's frame changed.
For more information about view hierarchy. See this article
Try like this:-
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for button in buttons {
button!.layer.shadowColor = UIColor.black.cgColor
button!.layer.shadowOffset = CGSize(width: 0, height: 1.0)
button!.layer.shadowOpacity = 0.4
button!.layer.shadowRadius = button!.frame.height / 40
button!.layer.cornerRadius = button!.frame.height / 10
}

How do I add constraints so that my view's dimensions do not change when the orientation changes?

I want my view to have the following properties (the numbers are arbitrarily chosen):
width is equal to height divided by 1.2
stays at the bottom right of the screen
height is 1/7 of the screen's height when in portrait
width and height does not change when the orientation changes
The first three requirements can be easily translated into UILayoutConstraints. I have done them with SnapKit just because it reads more clearly. You should see what I mean even if you have never used SnapKit before.
let myView = UIView(frame: .zero)
myView.backgroundColor = .green
view.addSubview(myView)
myView.snp.makeConstraints { (make) in
make.right.equalToSuperview().offset(-8)
make.bottom.equalToSuperview().offset(-8)
make.width.equalTo(myView.snp.height).dividedBy(1.2)
make.height.equalTo(view.snp.height).dividedBy(7) // *
}
The problem is the last bullet point. When I rotate the device from portrait to landscape, what was originally the width before the rotation, becomes the height after the rotation. This causes my view to become smaller as a result.
Basically, I want to replace the constraint marked with * with something like this:
make.height.equalTo(max(view.snp.height, view.snp.width)).dividedBy(7)
but I don't think max(a, b) is a thing in SnapKit or the UILayoutConstraint API.
Surely there is some other way of expressing "equal to whichever length is longer", right?
P.S. I didn't tag this with snapkit because I would also accept an answer that uses the UILayoutConstraint API.
Looks like you have 2 options:
Hardcode the height value.
Try to use nativeBounds:
This rectangle is based on the device in a portrait-up orientation. This value does not change as the device rotates.
In this case the height is always be for portrait mode.
myView.snp.makeConstraints { make in
make.right.bottom.equalToSuperview().offset(-8)
let screenHeight = UIScreen.main.nativeBounds.height / UIScreen.main.nativeScale
let height = screenHeight / 7
make.width.equalTo(height).dividedBy(1.2)
make.height.equalTo(height)
}

How to make sure uiview looks same in different ipads without using constraints

in below example i have mentioned
-view (UIView which i will be displaying in both iPads).
-parentView (container UIView that will be same as iPads resolution).
-parentViewOfIPadInWhichViewHasMade : this is parentview of iPads in which view has been saved.this will be used when displaying view in different iPads.
function is i am saving view in iPad mini and saving it will store its frame information on server and when open app from iPad pro it will fetch those data from server and display view in iPad pro.
this is my expected output if view has been resized to fullscreen of iPad Pro and Fetched From iPad Mini
iPad Pro 12.9 inch :
view.size (x : -25.0, y : -25.0, width : 1416.0, height : 874.0)
parentView.size (x : 0.0, y : 0.0, width : 1366.0, height : 824.0)
iPad Mini :
view.size : (x : -25.0,y : -25.0, width : 1074.0, height : 618.0)
parentView.size : (x : 0.0, y : 0.0, width : 1024.0, height : 568.0)
to get this i have wrote below code in view did load.
viewX = parentView.frame.width * view.origin.x / parentViewOfIPadInWhichViewHasMade!.width
viewY = parentView.frame.height * view!.origin.y / parentViewOfIPadInWhichViewHasMade!.height
viewWidth = parentView.frame.width * view!.frame.width / parentViewOfIPadInWhichViewHasMade!.width
viewHeight = parentView.frame.height * view!.frame.height / parentViewOfIPadInWhichViewHasMade!.height
note : can't fix size of view or use Constraint.
I can suggest a couple of ideas (before coding..):
1) your graphic/creative design must precisely consider the layout as a UX point of view. After this huge work you can start coding.
2) You can of course use code without using constraints. Asa programmer you cn prefer to do math instead of using mouse. But be careful: Using constraints is much more flexible and you can mix code too.
3) a pervasive and deep approach can be to implement layoutSubviews in every view..
example:
override func layoutSubviews() {
super.layoutSubviews()
self.titleLabel?.sizeToFit()
self.subTitle?.sizeToFit()
self.callLabel?.sizeToFit()
if(self.subTitle?.text == nil) {
self.callLabel?.center.y = (self.titleLabel?.center.y)!
}
}
but honestly for basic needs, better to use constraints, and eventually add an IBOutlet so you can control in code, for example height in custom view:
final func setH(Hconstraint: NSLayoutConstraint!){
var h = Hconstraint!.constant
if (.... add your test based on models, orientation...){
h = H1
}else{
h = H2
}
Hconstraint.constant = h // re-apply height.
self.setNeedsDisplay() // to trigger redraw... seems needed in some cases...
}
(you can also add this code in controller.. but better to put it in custom view, so the logic is there..)
we need to use scale for this problem.
if we have saved view in ipad pro and opening in ipad mini than we need to set parentview's height and width from ipad pro parentview. and than scale down it to ipad mini parentview and calculate x and y cordinate change.

Swift 3.0: text column width does not always update when rotating device

I have recently inherited an an iOS app made in Swift 3.0 from a developer that no longer works here.
The app is made in Xcode and uses storyboards for some of the screens, but not all of them.
On a iPad in landscape orientation the main screen contains an image taking op 2/3 of the width, with a text column next to it taking up 1/3 of the screen. Below are three images each up 1/3 wide. On smaller screens all these items take up 100% of the available width and are displayed underneath each other as a long list.
This is done using the following code in MainController.swift:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews();
// On small screens, columnify the layout
let mainContent = view.viewWithTag(1) as! UIStackView; //contains large image and text
let thumbnails = view.viewWithTag(3) as! UIStackView; //three images
mainContent.axis = .horizontal
mainContent.distribution = .fillProportionally
thumbnails.axis = .horizontal
if(self.view.bounds.width < 1000) {
mainContent.axis = .vertical
mainContent.distribution = .equalSpacing
thumbnails.axis = .vertical
} else {
mainContent.axis = .horizontal
mainContent.distribution = .fillProportionally
thumbnails.axis = .horizontal
}
}
This all works well, unless the user performs the following actions:
Start in landscape orientation on iPad. Image is 2/3, text 1/3
Tap on thumbnail and navigate to a different panel.
Rotate iPad to portrait.
Navigate back to first panel.
Observe that all items are places underneath each other, the image is 100%, but text is only 1/3 of the screen, while it should be 100%
Rotate to landscape, text moves next to image.
Rotate to portrait, text now spans 100% (as it should be)
My hunch would be that manually triggering a re-layout after navigating back to the first panel would solve it, but I cannot find code related to the navigation. This seems all handled by some "Apple magic?". There is probably a way to hook into it, but without any code I don't have any pointers. My only other solution would be to try and refactor the entire application with storyboards, but before I start with that, I was hoping on getting some insights here.

UIbutton misplaced in simulator

Here I have to implement the UIView for both the screens iPhone 5 and 6. So I used the screeHeight to integrate the UIView. But the button in the view is misplaced in iPhone 5s simulator and works fine in iPhone 6 simulator Here is the code for that. Whatever I changed the values in x and y remains the same top position.
if screenHeight == 568{
self.viewFareEstimate.frame = CGRect(x:0, y:150, width:359, height:40)
}
The screen size of Iphone 5 is 320 × 568, but your width is more than 320.
Try this
if screenHeight == 568{
self.viewFareEstimate.frame = CGRect(x:0, y:150, width:320, height:40)
}
Hi #imac for good programming practices try to avoid to give constant values to frame.
For your refrence height for iPhone 5 is 568.0f & 6 is 667.0f are different,
&
Whenever you want size width full then try to give frame like this,
self.viewFareEstimate.frame = CGRect(x:0, y:150, width:self.view.frame.size.width, height:40);// Manage width by + or - constant value now.
In your case check that if you are giving constraint from storyboard already then it should work with setting frame only.

Resources