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
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.
I want to give a UIViewController's view a size that is different from the device's screen size. I know I can usually achieve this by adding the view controller as a child view controller of another parent UIViewController that has defined a frame for the child, but I am in a situation that seems a little different.
I have a UIWindow that only takes up a portion of the screen (it's got a frame that's basically (0, 0, DEVICE_WIDTH, HEIGHT_LESS_THAN_DEVICE_HEIGHT). This window shows up with the proper sizing and positioning when presented. I am setting a view controller as the rootViewController of the window, and then presenting the window by setting its hidden value to false. When this happens, the view controller's view ends up sized to fill the device's screen (i.e. a frame of (0, 0, DEVICE_WIDTH, DEVICE_HEIGHT)).
What I would like is for the view controller to inherit its sizing from the UIWindow it is set as the root view controller of. Is there a way to do this?
I have also tried overriding loadView() and returning a custom-sized view there. Logging the view shows that the view controller's view object is correctly sized during viewDidLoad, but is overwritten with the default size by viewWillAppear:. I would be open to using loadView() to size the view controller if inheriting sizing from the window isn't possible, but I don't know how to make the custom size stick.
Note: The reason why I am trying to add a view controller to the window is because I want to take advantage of the view controller lifecycle methods such as viewDidAppear:, which is why I am not just creating a simple UIView and adding it as a subview of the window.
As counter intuitive as it may seem, if you set set self.view.frame on viewWillAppear (IOS 8) or viewDidAppear (IOS 7) you will be able to make it work.
Swift code (IOS 8):
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// Banner style size, for example
self.view.frame = CGRectMake(0, 0, 320, 50)
}
For IOS 7, I had to use viewDidAppear, which is obviously an unsatisfactory solution. So I had to start the view with alpha = 0.0 and set alpha = 1.0 on viewDidAppear, after modifying self.view.frame.
Hope it helps.
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.
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).
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.