I got the following code in a VC in an old project (no storyboard, pure code) :
- (void)viewDidLoad {
[super viewDidLoad];
self.mapView = [[MKMapView alloc] initWithFrame:CGRectInset(self.view.frame, 10, 10) ];
[self.view addSubview:self.mapView];
self.view.backgroundColor = [UIColor redColor];
self.mapView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.view.translatesAutoresizingMaskIntoConstraints = NO; // <--- this line
}
If I comment the last line, a rotation from portrait to landscape or the other way is about 3 seconds under ios8 !! Also, occasionally at random times Unable to allocate render buffer storage! errors appear.
If I don't comment it, it's almost instantaneous (0.7seconds).
It seems it is only related to mapviews, the other views/VCs rotate just fine.
Under ios7 the rotation is fast in any case with that line commented or not.
Why ? And why is only the mapview affected ?
Edit: It seems clearly the autoresizingmask is wrong. If in viewDidLoad I set its value to none and manually change the frame in willRotate, it works fast.
I had the same problem when the application came back from the background. It froze a few seconds and then it worked again. I used a .xib file without Autolayout.
When I updated the .xib file and added the Autolayout option the problem was gone. I came up with this idea because you wrote that autoresizing was the problem in your solution.
Related
I have an project using autolayout,
And I notice that after viewWillAppear, viewWillLayoutSubViews and viewDidLayoutSubViews pair will be called several times on iOS 8, for my case, it is 2-3 times usually.
The fist viewDidLayoutSubViews will get incorrect frame size, so I have to avoid for first viewDidLayoutSubViews, and init my views afterwards.
However, when I tested it on iOS 7, I found that only ONE viewWillLayoutSubViews and viewDidLayoutSubViews pair got called, so my code broke again.
My question is, what is changed on iOS 8 for this behaviour?
EDIT:
I have pasted my demo code here:
In the code, _pieChart will be added to self.ChartViewCanvas, and self.ChartViewCanvas is using autolayout. _pieChart is from old project code, which is drawn without auto layout.
I was required to draw the pie chart before viewDidAppear, because drawing in viewDidAppear will have a 1 sec delay compare to other views in storyboard. This is not allowed for me.
Is there any way to know when is the final viewDidLayoutSubViews? Calling [self.ChartViewCanvas addSubview:_pieChart]; multiple times will lead to lower performance, and sometimes _pieChart's drawInRect will not be called every time, so the chart is not update.
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
_pieChart.delegate = self;
if (!_pieChart) {
_pieChart = [[PieChartView alloc] initWithFrame:CGRectMake(0, 0, pieRadius * 2, pieRadius * 2)];
}else {
[_pieChart setFrame:CGRectMake(0, 0, pieRadius * 2, pieRadius * 2)];
}
//_pieChart.translatesAutoresizingMaskIntoConstraints = NO;
if ([_pieChart superview]) {
[_pieChart removeFromSuperview];
}
[self.ChartViewCanvas addSubview:_pieChart];
}
Probably only Apple knows, but I won't deal with that too much if everything is working fine. In iOS8 Apple changed a lot view controllers (again) in they way they are presented from containers VC as for rotation and UITraitCollections.
For instance UIAlertView is now a view controller, when you show one you trigger all the mechanism related to present a VC.
If this fact is creating an issue it must be said that you should not rely on how many times those methods are called because they were always be unpredictable there are too many variables to be taken into account.
A quick and dirty solution could be wrap your code in a dispatch_once if you want that it will be called only one time.
If you add your view using auto layout correctly you won't see any sort of bug.
[EDIT]
Here is a little snippet about how it might look your viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
//.. your stuff
//We don't need any frame autolayout wil take care of calculating it on its pass
_pieChart = [[PieChartView alloc]initWithFrame:CGRectZero];
_pieChart.delegate = self;
_pieChart.translatesAutoresizingMaskIntoConstraints = NO;
[self.ChartViewCanvas addSubview:_pieChart];
NSDictionary *bindings = NSDictionaryOfVariableBindings(_pieChart);
// We create constraints to tell the view that it needs to sctretch its bounds to the superview
NSString *formatTemplate = #"%#:|[_pieChart]|";
for (NSString * axis in #[#"H",#"V"]) {
NSString * format = [NSString stringWithFormat:formatTemplate,axis];
NSArray * constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:bindings];
[_pieChart.superview addConstraints:constraints];
}
// Do any additional setup after loading the view.
}
Of course that is going to call drawRect:, draw rect is called when a view is marked as dirty in the display pass, but before display is usually called the autolayout engine to calculate frames of views in needs for layout.
I tried this out on my application and found the same as you: 1 call on iOS7 and 3 on iOS8. From the stack traces this seems to be down to doing double layout after viewWillAppear and an extra layout following viewDidAppear not seen on iOS7.
My suggestion would be that you add any views in viewDidLoad (or viewWillAppear), then only do layout adjustments in the layout subview runs. Based on your updated post something like:
- (void)viewDidLoad{
[super viewDidLoad];
_pieChart = [[PieChartView alloc] initWithFrame:CGRectMake(0, 0, pieRadius * 2, pieRadius * 2)];
[self.ChartViewCanvas addSubview:_pieChart];
_pieChart.delegate = self;
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[_pieChart setFrame:CGRectMake(0, 0, pieRadius * 2, pieRadius * 2)];
}
For interest the difference between iOS7 and 8 calling sequence was:
iOS7
i) viewWillAppear is called.
ii) layout of subviews is called. From the stack this seems to relate to the navigation bar and animation.
ii) viewDidAppear is called.
iOS8
i) viewWillAppear is called.
ii) layout of subviews is called. From the stack this seems to relate to the navigation bar and animation.
iii) exact same layout with exact same stack is called again. So something in the stack must request a rerun from some point.
iv) viewDidAppear is called.
v) An extra layout of subviews is called. This seems driven from a transaction pushed onto the run loop.
I have my app setup to show this view when it is loading data:
self.loadingView = [UIView new];
self.loadingView.frame = CGRectMake(0, 0, self.tableView.frame.size.width, self.view.frame.size.height);
self.loadingView.backgroundColor = [UIColor groupTableViewBackgroundColor];
[self.view addSubview:self.loadingView];
[self.view bringSubviewToFront:self.loadingView];
self.activityIndicator = [UIActivityIndicatorView new];
self.activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
self.activityIndicator.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0);
[self.view addSubview:self.activityIndicator];
[self.view bringSubviewToFront:self.activityIndicator];
[self.activityIndicator startAnimating];
Then, I remove it from its superview. It works on iPhone. It works on iPad sometimes too, except for when I'm using the same code in a UISplitViewController. I've tried various adjustments to centering the views, etc., but can't figure it out. What's going wrong?
I've add trouble with activity indicators in the past as well. Make sure you are not calling the startAnimating or stopAnimating while any animations are taking place. I recommend calling the startAnimating selector in viewDidLayoutSubviews.
The line where you set the center of the indicator looks like the source of the problem. self.view.frame.size is probably equal to screen size at this point and so when you show that view controller over the whole screen it's ok, but inside a split view controller it's not because indicator is off-bounds. You can check that from Xcode's Debug -> View Debugging -> Capture View Hierarchy (while the app is running).
Try setting activity indicator's center using autolayout and it should work.
It seems that one particular aspect of iOS programming is to diagnose these weird, seemingly trivial yet frustratingly obscure small problems.
So today, I was happily woking on my recent iOS project, and I decided to upgrade my project to the latest version ECSlidingViewController, what harm could it do right? Just update a few deprecated methods that's all.
So I did all of that. Everything works fine, beautiful. However, I noticed that the status bar is behaving strangely! It is not appearing when I display one of my underLeftViewController, and it is in a weird shade when I push segue that particular underLeftViewController into one of its subsequent VC. What?? How could this be happening? Anyway, a picture is worth a thousand words:
So here is it acting nice and normal:
Now it disappears!!!:
Now it has a weird shade!!!:
And here is a picture of the app with the sliding view controller slided out:
I must have done something crazy to my status bar somewhere, then I thought.
So I looked into my implementation file for the VC where statusbar is acting crazy. It is in fact a subclass of UINavigationController, and its viewDidLoad is empty except with the [super viewDidLoad] line. So nothing suspicious here.
The run test page is in fact the rootViewController for the navVC, so I looked into it. I put all of my view setup code in its viewDidLoad, and this is what it looks like:
- (void)viewDidLoad
{
[super viewDidLoad];
// remove advanced button
self.navigationItem.rightBarButtonItem = nil;
self.navigationController.view.layer.shadowOffset = CGSizeMake(1, 0);
// setupGaugeView
[self setupGauge];
// add run test button
[self setupRunTestButton];
// setup notification container
CGRect notificationContainerFrame;
if ([WRGlobalHelper currentDeviceVersion] >= 7) {
CGFloat statusBarHeight = [WRGlobalHelper statusBarHeight];
notificationContainerFrame = CGRectMake(0, [[self.navigationController navigationBar] bounds].size.height+statusBarHeight, self.view.bounds.size.width, 1);
for (UIView *subview in self.view.subviews) {
CGRect newFrame = subview.frame;
newFrame.origin.y += [WRGlobalHelper statusBarHeight];
subview.frame = newFrame;
}
} else {
notificationContainerFrame = CGRectMake(0, [[self.navigationController navigationBar] bounds].size.height, self.view.bounds.size.width, 1);
}
self.notificationContainerView = [[UIView alloc] initWithFrame:notificationContainerFrame];
self.notificationContainerView.clipsToBounds = NO;
self.notificationContainerView.layer.backgroundColor = [UIColor clearColor].CGColor;
[self.view addSubview:self.notificationContainerView];
// some other unrelated stuff omitted....
}
And the `viewDidLoad's for the VCs where the status bar is acting normal or bizarre but with shade is all quite plain as well, they look like
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
}
Mind blown and I give up at this point. I've spent nearly 2 hours on this single issue already, and my brain hurts at the thought of the disappearing status bar. The almighty and omniscient SO, please help me! Thank you very much!
The status bar is not disappering, the text is just changing its color based on its assigned Style.
This answer will help
Am not sure where an wrong. I have an UIImageView in Storyboard, which am connecting with an IBOutlet to the code. I want to add a shadow (during runtime) to the UIImageView. And am using this code snippet for that (got this help from a stackoverflow post) :
-(void) awakeFromNib {
self.imageViewTopBar.layer.shadowColor = [UIColor blueColor].CGColor;
self.imageViewTopBar.layer.shadowOffset = CGSizeMake(0, 5);
self.imageViewTopBar.layer.shadowOpacity = 1.0;
self.imageViewTopBar.layer.shadowRadius = 3.0;
//self.imageViewTopBar.layer.masksToBounds = YES;
//self.imageViewTopBar.clipsToBounds = NO;
self.imageViewTopBar.layer.shouldRasterize = YES;
}
but it simply doesn't work. Any help please? I don't see any reason why it shouldn't work.
My environment : XCode 4.5.2, iOS 5, iPhone 4
You should use -viewDidLoad to do this, check out this post: Accessing View in awakeFromNib?
Try putting it into viewDidLoad. I assume you've included the quartzcore framework. I think it would throw compiler error if not...
Do you have checked, in the xib, clipToBounds to YES?
If YES you should uncheck the clipToBounds and reorganize your view hierarchy.
Normally when i have an imageView with a shadow i use this hierarchy
(aImageView) ClipToBounds=YES
|
|
(aView) Shadow setted here
|
|
(Superview)
aView has the same frame size of the aImageView
I'm running into a tricky glitch.
I've got a .xib, which was created by XCode when I created a new UIViewController subclass. It has quite a few elements, including some UIButtons, a bunch of UILabels, a small UIView with some other views inside it, and a UIImageView containing a mostly-transparent image.
In viewDidLoad, I set the background color of the UIImageView to a color using a pattern image.
When I display this view controller in the simulator or on my iPhone 4 (both running iOS 5.1), everything goes smoothly; the patterned background displays, all the interactions work, and so on.
When I test on iOS 4.3, however (either in the simulator or on my iPod Touch 2G), it appears that everything I'm trying to manipulate based on an outlet (e.g. [self.myBackgroundImageView setBackgroundColor...] or [self.mySegmentedControl setEnabled:NO]) just doesn't work at all.
The only even vaguely unusual thing I'm doing when the view gets presented is this, which makes it size properly in a popover:
- (void) viewWillAppear:(BOOL)animated {
CGSize size = CGSizeMake(320, 480); // size of view in popover
self.contentSizeForViewInPopover = size;
[super viewWillAppear:animated];
}
I really can't think of anything else that might be the problem. I've cleaned, rebuilt, all that stuff. Still no dice.
Has anyone else encountered this?
UPDATE per request by ott:
Added the following at the end of -viewDidLoad:
NSLog(#"self.myBackgroundImageView = %# | %#", [self.myBackgroundImageView description], [[self.myBackgroundImageView backgroundColor] description]);
...The output is:
self.myBackgroundImageView = <UIImageView: 0x6d48c80; frame = (0 0; 320 480); autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x6d48b60>> | <UIImageView: 0x5f459b0; frame = (0 0; 320 480); autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x5f45890>> // kCGColorSpaceModelPattern 1
So... not nil. Not sure what the story is here.
UPDATE 2:
It appears the disabling of a UISegmentedControl in iOS 4.3 doesn't dim its display, so that's what that part was about. As for the background pattern image: I can't find confirmation of this, but I'm starting to think it's a bug in iOS 4 that makes a background color using a pattern-image UIColor not display properly on a UIImageView. It works fine if I make the UIImageView have a clear background and put the pattern image UIColor as the background of the main view instead. If anyone comes up with a workaround, or confirmation that this is indeed an iOS 4 bug, it would be much appreciated.