iOS part of viewcontroller goes black when orientation changes - ios

When I change viewcontroller orientation while it's loading on particular orientation, part of the screen goes blank at the bottom.
Steps to reproduce:
Have tableview(Not necessary I think. could be any view) in a view controller
Push a new viewcontroller which also has a tableview(Not necessary I think. could be any view) during
an action
Rotate the screen from one orientation to Another(Portrait to landscape or landscape to portrait).now you will be able to see the dark part.
Press back button to pop the current viewcontroller
Now rotate from one orientation to Another(Portrait to landscape or landscape to portrait).now you will be able to see the dark part here as well.
The bottom part of viewcontroller goes black.
I am able to reproduce 7 out of 10 times.
FYI:
My Viewcontroller has only tableview that's all and all cells has autolayout constraints.
Trying to understand why it happened and I would like to fix it.
More details on reproducing the issue:
Basically you can reproduce this issue only if you hold your device in slanting position and either push or pop a view controller and while the page is trying to load , you have to change the orientation then the issue happens.
(Pun intended.... lol )
In other words ,you have to tilt you device as if you are steering the wheel :).
This issue can only happen if you use your device like a steering wheel :)

In your viewWillTransition method call layoutIfNeeded()

Try to reset the layout of TableView after rotation by using this method,
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
tableView.layoutIfNeeded()
}
or
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.layoutIfNeeded()
}

I also experienced the same issue.
My view controller had just one tableview and when we tilt , I mean change the orientation during a navigational push or pop this used to happen.
Try setting the frame
in viewDidLayoutSubviews
-(void)viewDidLayoutSubviews{
CGRect screen = [[UIScreen mainScreen] bounds];
CGFloat width = CGRectGetWidth(screen);
CGFloat height = CGRectGetHeight(screen);
CGRect frame = self.tableView.frame;
// To check if there's any mismatch between the sizes of screen and tableview then set the tableview frame same as screen frame.
if (CGRectGetHeight(frame) != height && CGRectGetWidth(frame) != width) {
self.tableView.frame = CGRectMake(0, 0, width, height);
}
}
or try setting the frame in viewWillTransitionToSize
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
CGRect screen = [[UIScreen mainScreen] bounds];
CGFloat width = CGRectGetWidth(screen);
CGFloat height = CGRectGetHeight(screen);
CGRect frame = self.tableView.frame;
if (CGRectGetHeight(frame) != height && CGRectGetWidth(frame) != width) {
self.tableView.frame = CGRectMake(0, 0, width, height);
}
}
FYI:
if setting the frame self.tableView.frame = CGRectMake(0, 0, width, height); doesn't work then you could try [self.tableView layoutIfNeeded]

Set frame again in
viewDidLayoutSubviews

In one view controller(Parent the one that pushes new view controller) setting the frame at viewDidLayoutSubviews solves the issue in another view controller(The child view controller , one that's getting pushed) setting the frame at viewWillTransition fixes the problem :) But still I don't know why there's no universal solution to this problem.

Related

UiView frame not working

I have a UIView,
UIView *topBarView
I am deciding the size i.e. the width and height of topBarView as
CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height*.05);
Now I am setting the frame of topBarView as
topBarView.frame = (CGRect){0, 0,topBarSize};
Im my view controller I have set this condition for rotation
- (BOOL)shouldAutorotate
{
return YES;
}
-(UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
When my view controller opens in portrait mode it is all good and as expected the topBarView is placed at the top
But when my view opens in Landscape Left mode the topBarView instead of being on top in Landscape is on the left of the screen i.e same frame as it was in case of portrait
How can I fix this?
As you have set frame your view's origin is (0,0) so it will definitely set in top - left corner in any orientation.
If you want to manage width and height according to orientation then you should use autolayout.
You should set top,leading,trailing and fixed height constraint to that view. so, it will will manage height,width and position with orientation.
Update:(as asked in comment)
Write below code in viewdidload and comment yours
UIView *topBarView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height * 0.5)];
topBarView.backgroundColor = [UIColor orangeColor];
[self.view addSubview: topBarView];
Hope this will help :)

UITableView frame size strange behaviour

I try to set frame size to my table view programmatically after viewDidLoad:
[_tableView setFrame:CGRectMake(0, topHeight,
[[UIScreen mainScreen] bounds].size.width,
[[UIScreen mainScreen] bounds].size.height - topHeight)];
Default size is 200x200 with origin Y 100:
Scene hierarchy:
Implementation of UITableView shows when setFrame was called:
#implementation MainTableView
- (void)setFrame:(CGRect)frame {
NSLog(#"setFrame: %f %f", frame.size.width, frame.size.height);
[super setFrame:frame];
}
#end
All is fine but table's frame resizes to default when I press any cell without printing log messages from setFrame. Small explanation video: http://youtu.be/JUW_rHvCS2I
I don't understand why my table size becomes default (200x200) after cell click. Even if I try to set size inside viewWillAppear (after return from cell detail view) it doesn't work.
Any ideas?
I can't really tell why your tableView isn't resizing properly since you haven't provided much code to look around. Though I recommend setting some constraints or resizing rules for the tableView, probably something like this:
http://i.imgur.com/N9fvvIB.png (if not using Auto Layout)
http://i.imgur.com/3Uz8e5e.png (if using Auto Layout)
Now if you really need to make this work only trough code I suggest throwing up some NSLog's containing the tableView's frame width and height before and after setting it up.
Try _tableView.translatesAutoresizingMaskIntoConstraints = NO.

Why a frame of a UIView is not updating in ViewDidLayoutSubviews?

I am trying to update the frame of a UIView which contains buttons and labels inside. I am trying to update it in viewDidLayoutSubviews (and I also tried in viewDidLoad, viewWillAppear, viewDidAppear..). I want to change the y position (origin.y) of the view.
The NSLogs says my original y position is 334, and after changing, it is 100. However, the position does not change in my view. I have already checked that the view is connected in the storyboard. What am I doing wrong?
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
CGRect theFrame = [self.bottomView frame];
NSLog(#"Y position bottomview: %f", self.bottomView.frame.origin.y);
if([[UIScreen mainScreen] bounds].size.height == 568) //iPhone 4inch
{
// NSLog(#"iphone5");
}
else{
// NSLog(#"iphone4");
theFrame .origin.y = 100;
}
self.bottomView.frame = theFrame;
NSLog(#"Y position bottomview after changing it: %f", self.bottomView.frame.origin.y);
[self.view layoutIfNeeded];
}
I've had the same problem. Forcing the layouting for your view's superview helped me out:
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self.bottomView.superview setNeedsLayout];
[self.bottomView.superview layoutIfNeeded];
// Now modify bottomView's frame here
}
In Swift:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
bottomView.superview!.setNeedsLayout()
bottomView.superview!.layoutIfNeeded()
// Now modify bottomView's frame here
}
Believe it or not the below code fixed it
override func viewDidLayoutSubviews() {
DispatchQueue.main.async {
// UI changes
}
}
The problem was related with Autolayout. However I couldn't turn it off since I am using autolayout in my project. I solved defining appropriate constraints in the view. Then there is no need to check if it is iPhone 4inch or 3.5inch and change the position of the frame since it automatically adapts to each size.
The frame setting should work in your code. But if the view has autolayout constraints (which I assume you have), your frame setting won't work. You can only go one way or the other (manual frame setting or autolayout), not both.

Screen size minus advertising size

How can I get the screen height minus the height of the ads I'm displaying?
I get my screen size with
[[UIScreen mainScreen] bounds].size;
And I'm showing one banner ad:
self.canDisplayBannerAds = YES;
So the screen height should be something like:
[[UIScreen mainScreen] bounds].size.height - banner.height;
I want to place a object in the exact middle of the screen and the banner isn't showing all the time.
canDisplayBannerAds will show an ad that you can't adopt its delegate what so ever. Also you cant reference the ad view to get frame height or width. I suggest using old fashioned way. I can post a code if you want.
Getting the size of the screen isn't really what you want to do. This is because if the device turns sideways (if you are supporting rotation) or if your view controller does not take up the entire screen (like if your view controller is in a UINavigationController or UITabBarController) your banner will be off the screen.
To place the banner at the bottom of the viewControllers view and place an other view in the middle of a viewControllers view you could write something like this
-(void) viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
CGRect bounds = self.view.bounds; // for a normal view controller {0,0,width,height}
CGRect bannerRect = self.banner.frame;
bannerRect.origin.y = bounds.size.height - bannerRect.size.height; // bottom of the screen
bannerRect.origin.x = (bounds.size.width - bannerRect.size.width)/2.0; // centre horizontally
self.banner.frame = bannerRect;
CGRect objectRect = self.objectView.frame;
objectRect.origin.y = (bounds.size.height - objectRect.size.height)/2.0; // centre vertically
objectRect.origin.x = (bounds.size.width - objectRect.size.width)/2.0; // centre horizontally
self.objectView.frame = objectRect;
}

UIScrollView adjusts contentOffset when contentSize changes

I am adjusting a detail view controller's state, just before it is pushed on a navigationController:
[self.detailViewController detailsForObject:someObject];
[self.navigationController pushViewController:self.detailViewController
animated:YES];
In the DetailViewController a scrollView resides. Which content I resize based on the passed object:
- (void)detailsForObject:(id)someObject {
// set some textView's content here
self.contentView.frame = <rect with new calculated size>;
self.scrollView.contentSize = self.contentView.frame.size;
self.scrollView.contentOffset = CGPointZero;
}
Now, this all works, but the scrollView adjusts it's contentOffset during the navigationController's slide-in animation. The contentOffset will be set to the difference between the last contentSize and the new calculated one. This means that the second time you open the detailsView, the details will scroll to some unwanted location. Even though I'm setting the contentOffset to CGPointZero explicitly.
I found that resetting the contentOffset in - viewWillAppear has no effect. The best I could come up with is resetting the contentOffset in viewDidAppear, causing a noticeable up and down movement of the content:
- (void)viewDidAppear:(BOOL)animated {
self.scrollView.contentOffset = CGPointZero;
}
Is there a way to prevent a UIScrollView from adjusting its contentOffset when its contentSize is changed?
Occurs when pushing a UIViewController containing a UIScrollView using a UINavigationController.
iOS 11+
Solution 1 (Swift Code):
scrollView.contentInsetAdjustmentBehavior = .never
Solution 2 (Storyboard)
iOS 7
Solution 1 (Code)
Set #property(nonatomic, assign) BOOL automaticallyAdjustsScrollViewInsets to NO.
Solution 2 (Storyboard)
Uncheck the Adjust Scroll View Insets
iOS 6
Solution (Code)
Set the UIScrollView's property contentOffset and contentInset in viewWillLayoutSubviews. Sample code:
- (void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
self.scrollView.contentOffset = CGPointZero;
self.scrollView.contentInset = UIEdgeInsetsZero;
}
The cause of this problem remains unclear, though I've found a solution. By resetting the content size and offset before adjusting them, the UIScrollView won't animate:
- (void)detailsForObject:(id)someObject {
// These 2 lines solve the issue:
self.scrollView.contentSize = CGSizeZero;
self.scrollView.contentOffset = CGPointZero;
// set some textView's content here
self.contentView.frame = <rect with new calculated size>;
self.scrollView.contentSize = self.contentView.frame.size;
self.scrollView.contentOffset = CGPointZero;
}
I had the same issue with a UIScrollview, where the problem was caused by not setting the contentSize. After setting the contentSize to the number of items this problem was solved.
self.headerScrollView.mainScrollview.contentSize = CGSizeMake(320 * self.sortedMaterial.count, 0);
Here's what worked for me:
In the storyboard, in the Size Inspector for the scrollView, set Content Insets Adjustment Behavior to "Never".
Is your scrollView the root view of the DetailViewController? If yes, try wrapping the scrollView in a plain UIView and make the latter the root view of DetailViewController. Since UIViews don't have a contentOffset property, they are immune to content offset adjustments made by the navigation controller (due to the navigation bar, etc.).
I experienced the problem, and for a specific case - I don't adjust the size - I used the following:
float position = 100.0;//for example
SmallScroll.center = CGPointMake(position + SmallScroll.frame.size.width / 2.0, SmallScroll.center.y);
Same would work with y: anotherPosition + SmallScroll.frame.size.height / 2.0
So if you don't need to resize, this is a quick and painless solution.
I was experiencing a similar problem, where UIKit was setting the contentOffset of my scrollView during push animations.
None of these solutions were working for me, maybe because I was supporting iOS 10 and iOS 11.
I was able to fix my issue by subclassing my scrollview to keep UIKit from changing my offsets after the scrollview had been removed from the window:
/// A Scrollview that only allows the contentOffset to change while it is in the window hierarchy. This can keep UIKit from resetting the `contentOffset` during transitions, etc.
class LockingScrollView: UIScrollView {
override var contentOffset: CGPoint {
get {
return super.contentOffset
}
set {
if window != nil {
super.contentOffset = newValue
}
}
}
}
Adding to KarenAnne's answer:
iOS 11+
automaticallyAdjustsScrollViewInsets was deprecated
Use this istead:
Storyboards:
Code (Swift):
scrollView.contentInsetAdjustmentBehavior = .never

Resources