Prevent animation when dynamically resizing popover - ios

So I realized this myself and found this answer to confirm:
Prevent contentSizeForViewInPopover from animating
Basically I am in a similar boat in that I want to dynamically resize my popover, depending on how much data I have to display. I am also getting this animation action where the popover view moves into place. The problem is, I can't set the popover's content size in the caller/parent, because neither the size of the content view for the text view nor the frame for the text view in my popover is known until viewDidAppear or viewDidLayoutSubviews is called, but of course at this point it's too late, the popover is already on its way to being visible, thus I am getting this unwanted animation.
I am dynamically setting the size of the text views in the popover view controller class to fit the amount of data the text views are displaying, and this only works at the point where their frame size is defined/known/however you want to word it when it hasn't gotten to that point yet in the view lifecycle.
I should mention that I have my view controllers and view all done in a storyboard.
Does this make sense? Hope I'm explaining it well. Any help or suggestions greatly appreciated.
EDIT Here is my viewDidLayoutSubviews method from the view controller I am loading in the popover - hopefully this makes it clear. The first time this loads, if I don't do this in viewDidLayoutSubviews or viewDidAppear the frame for my 2 text views are CGRectZero, the text views don't resize for the content correctly and the popover size isn't right either:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.physicianDataNewValueTextView.text = self.physicianDataNewValue;
CGRect frame = self.physicianDataNewValueTextView.frame;
frame.size.height = self.physicianDataNewValueTextView.contentSize.height;
self.physicianDataNewValueTextView.frame = frame;
self.physicianDataOldValueTextView.text = self.physicianDataOldValue;
frame = self.physicianDataOldValueTextView.frame;
frame.size.height = self.physicianDataOldValueTextView.contentSize.height;
self.physicianDataOldValueTextView.frame = frame;
self.contentSizeForViewInPopover = CGSizeMake(384.0f, self.physicianDataOldValueTextView.frame.size.height + 75.0f);
}

So I decided to use delegation. Instead of resetting the content size in the view controller for the popover, I call my delegate (which is the containing view controller) with the new size so I can set it there with the animated property set to NO. Works well.

Related

How to prevent iOS from resizing your UIViewController's view after return from background

I have a UIViewController that displays a form with several text fields. In order to prevent the text fields from getting blocked by the keyboard, I resize the controller's view when the keyboard appears and disappears.
However, when the keyboard is up, the user presses the home button, and then returns to the app, the controller's view will be resized again to the size it was before the keyboard was up and the keyboard will still be showing.
What's causing my controller's view to be resized on return from background, and how can I prevent it?
Maybe you need to nest a UIView,for example
_backgroundView = [UIView new];
_backgroundView.backgroundColor = [UIColor whiteColor];
_backgroundView.frame = CGRectZero;
[self.view addSubview:_backgroundView];
[_backgroundView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.top.mas_equalTo(self.view);
make.height.mas_equalTo(self.view.mas_height);
}];
then you need add your custom UIView to this backgroundView.
as you said,UIViewController's view will be resized after return from background. so you can nest a UIView of the same size as self.view,and add your custom UIView to this UIView.
In order to prevent the text fields from getting blocked by the keyboard, you can resize this backgroundView when the keyboard appears and disappears. and this time when you click the home button to enter the background or return from background,self.view won't be resized and backgroundView won't be resized too.
Although it is a bit of a hassle, this will solve your problem and will not affect the user experience anymore. And if you have a better solution, please let me know
It sounds like you are setting the frame and not using autolayout. When the view reappears viewDidLayoutSubviews gets called and your frame gets recalculated obliterating your previous change. You can either:
1) Move your frame to viewDidLayoutSubviews and change its size only if the keyboard is showing.
2) Use autolayout and simply pull up your bottom constraint .constant by an amount equal to your keyboard height.
In both cases you should call layoutIfNeeded to trigger autolayout/viewDidLayoutSubviews when the keyboard appears/disappears. This behavior is a good example of why you should not manipulate your frames outside of viewDidLayoutSubviews except for transitory animations.

iOS: Resizing popover dynamically [duplicate]

So I realized this myself and found this answer to confirm:
Prevent contentSizeForViewInPopover from animating
Basically I am in a similar boat in that I want to dynamically resize my popover, depending on how much data I have to display. I am also getting this animation action where the popover view moves into place. The problem is, I can't set the popover's content size in the caller/parent, because neither the size of the content view for the text view nor the frame for the text view in my popover is known until viewDidAppear or viewDidLayoutSubviews is called, but of course at this point it's too late, the popover is already on its way to being visible, thus I am getting this unwanted animation.
I am dynamically setting the size of the text views in the popover view controller class to fit the amount of data the text views are displaying, and this only works at the point where their frame size is defined/known/however you want to word it when it hasn't gotten to that point yet in the view lifecycle.
I should mention that I have my view controllers and view all done in a storyboard.
Does this make sense? Hope I'm explaining it well. Any help or suggestions greatly appreciated.
EDIT Here is my viewDidLayoutSubviews method from the view controller I am loading in the popover - hopefully this makes it clear. The first time this loads, if I don't do this in viewDidLayoutSubviews or viewDidAppear the frame for my 2 text views are CGRectZero, the text views don't resize for the content correctly and the popover size isn't right either:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.physicianDataNewValueTextView.text = self.physicianDataNewValue;
CGRect frame = self.physicianDataNewValueTextView.frame;
frame.size.height = self.physicianDataNewValueTextView.contentSize.height;
self.physicianDataNewValueTextView.frame = frame;
self.physicianDataOldValueTextView.text = self.physicianDataOldValue;
frame = self.physicianDataOldValueTextView.frame;
frame.size.height = self.physicianDataOldValueTextView.contentSize.height;
self.physicianDataOldValueTextView.frame = frame;
self.contentSizeForViewInPopover = CGSizeMake(384.0f, self.physicianDataOldValueTextView.frame.size.height + 75.0f);
}
So I decided to use delegation. Instead of resetting the content size in the view controller for the popover, I call my delegate (which is the containing view controller) with the new size so I can set it there with the animated property set to NO. Works well.

Weird interface bug UIScrollView in UITabBarController. Reproducible by others?

I get a weird interface bug with my UIScrollView and I cant figure out how to solve it. I only wrote one line of code (shown below) and it is a blank project's setup easily reproducible!
Setting:
I have a UIScrollView that contains a UISegmentedControl (since the segments of
the control are loaded dynamically, it could exceed the width of the screen and the scrollView is supposed to scroll the segmentedControl horizontally, the height of the scrollview is the same as the UISegmentedControl's).
The ViewController that contains this is embedded in a tabBar (or navigation bar, which also shows the bug). The whole thing is using Auto-Layout.
Bug:
When I scroll the SegmentedControl some degree to the right and then switch the viewController by clicking the other tab on the tabBarController, the content-offset of the segmented control gets weirdly shifted when switching back to the initial viewcontroller. When I try to scroll to the leftmost part of the scrollview it won't let me. When switching the tabs a couple of times, it gets fixed again and I can do this over.
What I did (can you reproduce this?):
Create a blank single-view ios project
Embed the already given viewController in a tabbarcontroller.
Put a scrollView on the upper portion of the view that fits the screen from left to right.
Put a UISegmentedControl on the topleft corner of the scrollview and drag the scrollview to fit the segmented controls height height
Change the Segmented control's width a bit so xcode adds a width-constraint. in the segmented control's width constraint change the width constraint's relation to "greater than or equal"
create an outlet to the segmented control
in viewDidload add this code
[self.segmentedControl insertSegmentWithTitle:#"A really long title so it you have to scroll to see it" atIndex: 0 animated: NO];
Create a blank viewcontroller and add it as a second viewController for the tabbarController.
This is how my storyboard looks like:
Now run the project, scroll the segmented control to it's right end as far as it goes. Switch the tab and switch back and please tell me how your scrollview now behaves - and WHY.
My guess would be it has something to do with Auto Layout maybe? Can't figure out what though.
I tried fixing this by setting the scrollView's contentSize in viewDidAppear or changing the content offset of the scrollView in viewDidAppear or changing frames, combination of those and what not....
Extra question:
Is it no longer neccessary to set the scrollViews contentSize property? Why does it scroll the content automatically?
After googeling I found the answer in another StackOverflow question.
What you need to do is save the scrollview.contentOffset on viewWillDisappear,
set it to CGPointZero on viewDidDisappear and set it back to the saved state on viewDidLayoutSubviews:
-(void) viewWillDisappear: (BOOL) animated {
self.lastContentOffset = self.scrollView.contentOffset;
[super viewWillDisappear: animated];
}
-(void) viewDidDisappear:(BOOL)animated {
[super viewDidDisappear: animated];
self.scrollView.contentOffset = CGPointZero;
}
- (void)viewDidLayoutSubviews {
[super viewDidlayoutSubviews];
self.scrollView.contentOffset = self.lastContentOffset;
}

UISearchDisplayController change dimmed view frame

In my iPad app I have an UITableView. Table's frame size is less than screen size, so to make search functionality look nice I have to adjust searchResultTableView's frame to fit my TableView. I'm doing it in my UISearchDisplayDelegate's -searchDisplayController:willShowSearchResultsTableView: method.
Everything works fine except dimming view. When I'm starting search dimming view's width is equal to screen width:
When I start entering search string or clear textfield my searchResultsTableView resizes properly and everything works as it should:
I tried to change searchResultsTableView frame inside -searchDisplayControllerWillBeginSearch: method using this line
controller.searchResultsTableView.frame = myFrame;
but it doesn't work as well. Any suggestions besides implementing my own search display controller?
I also needed to change the frame of the dimming view but for a different reason. In my case I created a UISearchDisplayController and UISearchBar programmatically in a regular UIViewController not a UITableViewController. I was also using MFSideMenu which added to the complexity of the problem. What ended up happening was the dimming view was in the correct position initially but the next time the search was cleared the dimming view shifted leftwards and upwards by exactly half of it's size. Given the UISearchDisplayController you can find the dimming view like so.
for(UIView * v in controller.searchContentsController.view.subviews)
{
if([v isMemberOfClass:[UIControl class]])
{
v.frame = newFrame; //This frame should account for the UISearchBar
}
}
To handle for the initial incorrect frame you should change it in this delegate method:
- (void) searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView;
To handle for an incorrect frame on any subsequent clears you should change it in this delegate method:
- (void) searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView;
Note: this solution runs through the subviews of the searchContentsController which is one of the reasons I used isMemberOfClass instead of isKindOfClass (UIButton is a subclass of UIControl). Further discrimination would be required if you added a UIControl instance into your view (you could use tags to help determine which ones are yours).

UITableViewCell frame changes while presenting parent view as modal with UIModalPresentationFormSheet

State of code:
I'm writing my own library for forms in table view (I'm aware of Sensible Cocoa but willing to write my own) and I'm dealing with an issue loosely connected to it.
My form is held by UITableViewController in UITableViewStyleGrouped style. This table view controller is created in a button handler with this code (initialization of form model ommited):
// prepare table view model
_formTableViewController = [[ESCTableViewController alloc] initWithModel:model];
_formTableViewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self action:#selector(cancelBarButtonItemHandler:)];
_formTableViewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit
target:self action:#selector(editBarButtonItemHandler:)];
// prepare navigation controller containing the tableViewController
ESCNavigationController *navigationController = [[ESCNavigationController alloc] initWithRootViewController:_formTableViewController];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
// present navigation view controller
[self presentViewController:navigationController animated:YES completion:nil];
Then, I have UITableViewCell subclassed (as ESCTableViewCell) with style UITableViewCellStyleDefault and each form component is a descendant of ESCTableViewCell using its basic initializer and few properties.
I'm doing my custom rendering like this:
Create my custom field (i.e. UITextField) in initializer of specific cell (ESCTextFieldCell)
prepareForReuse is called by framework on cell reuse, where I remove old observers and invalidate what is needed to invalidate
configWithModel:(Model *)model, where I set properties of the cell, add observers etc.
layoutSubviews is called by framework; meta-code of this process is here:
CGFloat const ESCTextFieldWidthRatio = 0.65f;
CGFloat const ESCFieldHeight = 22.0f;
CGFloat const ESCFieldPadding = 10.0f;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
// call super layout subviews first
[super layoutSubviews];
// count width of the text field, so it would fit with label
// must round() the results to avoid antialiasing on fractional values
CGFloat contentWidth = self.contentView.frame.size.width;
CGFloat contentHeight = self.contentView.frame.size.height;
CGFloat fieldWidth = round((contentWidth - (2 * ESCFieldPadding)) * ESCTextFieldWidthRatio);
CGFloat fieldOriginY = round((contentHeight / 2) - (ESCFieldHeight / 2));
CGFloat labelWidth = contentWidth - fieldWidth - (4 * ESCFieldPadding);
// set textlabel and textfield frames
self.textLabel.frame = CGRectMake(ESCFieldPadding, 0.0f, labelWidth, contentHeight);
self.textField.frame = CGRectMake(labelWidth + (3 * ESCFieldPadding), fieldOriginY, fieldWidth, ESCFieldHeight);
[UIView commitAnimations];
The Problem:
I'm trying to have the UI as clean as possible, being a perfectionist; thus I want to have table view cells animated on entry/leave of edit mode, which works pretty well in current state of code.
The issue is with presenting the modal view itself with this form table view inside. It seems (proven by simulator - slow animations), that when presenting the modal view, frame of the table view is updated (contracted) by (at least) a few pixels, which is causing both modal view translation animation AND table view cell resize animation, which is at the end causing a not-so-smooth animation of that modal view presentation.
What I would like to understand is, why the table view cell is resizing on modal view presentation as I don't like the idea to hack the code to disable animations when presenting the modal view (it can possibly cause lots of other issues later).
Please, does someone know why this is happening, or do you have some other idea how to override it non-hacky way?
Thank you a lot!
PS: I'm using ARC, but I don't think it matters.
PS2: Problem occures on both iPad1 and iOS Simulator for iOS5.
Miroslav, I'm not sure this helps, but I've been fighting the same thing. And I'm sharing this here because I assume others might wonder what's happening.
It's not just the cell, it's the entire tableview bounds that aren't settled until ViewDidAppear is called.
So let's say you want to bring up a 500x500 modal with a UITableViewController, you launch it with presentViewController, you will have all kinds of resizing going on until it's finally resolved in ViewDidAppear. In my case I've seen it through debugging go from 1024 wide to 320 then finally to 500. And it's that width that I need to determine the resulting height of the table view cells.
It all seems like a hack in this case and I'm not sure there is a clean solution though I wish for one. One way in which I'm testing it now, is that the custom table view cells have a "final width" property that they look at (if it's set) when sizing in layoutSubviews. That way the proper height of the cell can be calculated (based on this fixed width) at anytime prior to the tableview being in it's final form.
I've seen the same issue in some original (read Apple) applications on iOS, so I consider it a feature of the system. It seems, the best we can do, is to hide it as much as possible. Actually, nobody except me noticed the behavior, so I think nobody is bothered.
Consider it not an answer, but a closure. If you have some real solution for the problem, please write it here and I'll mark it as an answer.
Set the UITableView layout to delete outside autosizing.

Resources