UISearchBar - scope bar animated - iphone

I've found example of animated search-bar with scope bar under "UISearchDisplayDelegate Protocol Reference"(SearchBar-animated-sample
), here is a video preview:
SearchBarAnimated-video
I've checked sample code, but I can't find the code that triggers animation.
Does anyone knows how to create that animation ? Do you have to use UISearchBarDelegate to get that animation ?

Inorder to control the animations of UISearchBar you have implement the delegates of UISearchDisplayController by extending in your header file. The delegates are as follows;
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
[UIView beginAnimations:nil context:NULL];
self.searchDisplayController.searchBar.showsScopeBar = NO;
CGRect headerViewFrame = self.searchDisplayController.searchBar.frame;
headerViewFrame.origin.y -= 54.0f;
self.searchDisplayController.searchBar.frame = headerViewFrame;
CGRect tableViewFrame = self.tableView.frame;
tableViewFrame.origin.y -= 54.0f;
self.tableView.frame = tableViewFrame;
[UIView commitAnimations];
}
-(void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
[UIView beginAnimations:nil context:NULL];
CGRect headerViewFrame = self.searchDisplayController.searchBar.frame;
headerViewFrame.origin.y += 54.0f;
self.searchDisplayController.searchBar.frame = headerViewFrame;
CGRect tableViewFrame = self.tableView.frame;
tableViewFrame.origin.y += 54.0f;
self.tableView.frame = tableViewFrame;
[UIView commitAnimations];
}

It's built right in to UISearchBar. Apple does this for you, you don't have to call any method by yourself.
Basically, from the moment you set your search bar's scopeButtonTitles property, Apple will animate the scope bar.

I found the answer to this question more useful though it doesn't automatically transition the search bar to the top of your view.
How do you hide/show UISearchBar's scope bar with animation?

This works well for me in Xcode 6. If you have auto layout constraints in place then you may need to add in adjustments for them as I've done (didn't work without them).
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
{
searchBar.showsScopeBar = YES;
searchBarHeightConstraint.constant = 88; // Changes from 44 to 88 with scope bar
tableViewHeightConstraint.constant = 480; // Changes from 524 to 480 with scope bar
[UIView animateWithDuration:0.3
animations:^{
CGRect newFrame = tableView.frame;
newFrame.origin.y = 88;
tableView.frame = newFrame;
}];
return YES;
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
{
searchBar.showsScopeBar = NO;
searchBarHeightConstraint.constant = 44;
tableViewHeightConstraint.constant = 524;
[UIView animateWithDuration:0.3
animations:^{
CGRect newFrame = tableView.frame;
newFrame.origin.y = 44;
tableView.frame = newFrame;
}];
return YES;
}

sizeToFit in an Animation block
The UISearchBar w/ scope bar is easily animated. UISearchBar has a height of 44.f before calling sizeToFit with the scope bar and then becomes 88.f. In my case, the UISearchBar was embedded in a UITableView within Interface Builder so it was not possible to add auto layout constraints.
#pragma mark - UISearchBarDelegate methods
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
{
searchBar.showsScopeBar = YES;
[UIView animateWithDuration:0.2f animations:^{
[searchBar sizeToFit];
}];
[searchBar setShowsCancelButton:YES animated:YES];
return YES;
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
{
searchBar.showsScopeBar = NO;
[searchBar sizeToFit];
[searchBar setShowsCancelButton:NO animated:YES];
return YES;
}

Related

Cannot change UIView frame size

I have a custom uiview that i am trying to update the frame size. I do it like this in a method once it is clicked. The method is called, and the frame value changes, however on the screen nothing happens!
if (!touched) {
GameBox *box = (GameBox *)[self.view viewWithTag:40];
[box setFrame:CGRectMake(20, 20, 100, 73)];
NSLog(#"%f", box.frame.origin.x);
markButton.enabled = NO;
guessButton.enabled = NO;
[self.view reloadInputViews];
NSLog(#"The action bar should now be hidden");
}
else if (touched) {
guessButton.enabled = YES;
markButton.enabled = YES;
[self.view reloadInputViews];
NSLog(#"The action bar should now be visible");
}
I guess you have hooked self.view to UIView in Interface Builder.
In order to change UIView frame, in Interface Builder go to File Inspector, and uncheck Use Autolayout, then in the code change the frame.
Hope this helps.
You need to set adjust the frame in the viewDidAppear (not viewDidLoad).
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
CGRect frame = self.tableView.frame;
frame.origin.y += 100.0;
frame.size.height -= 100.0;
self.tableView.frame = frame;
}
Apparently this has something to do with table views within navigation controllers.
It is because of Auto Layout, however you could do it after Auto Layout done its work or change constraints of it. If you want to do it after auto layout add following.
- (void)viewDidAppear:(BOOL)animated {
dispatch_async(dispatch_get_main_queue(), ^{
box.frame = CGRectMake(20, 20, 100, 73)];
});
[self.view reloadInputViews] is generally used for FirstResponders. I Don't have much of an idea about your customview, but I think for just settings the frame you can use:
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelay:1.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
viewFrame.frame=frame;//Frame Defined by you
[UIView commitAnimations];
can give better solution if i get a look at your custom view.

UITableViewController with picker view

I've a serious problem, and I cannot solve it by myself. I've spent hours searching the documentations, programming guides as well as developer forums and stack overflow.
The problem is I want to display a picker view in UITableViewController. I have a screen that has multiple text fields allowing me to search by title/author/keywords... and I'd also like to specify the minimum and maximum dates, using the UIDatePicker (or UIPickerView - to specify "last 5 days" for example).
I want to use UITableViewController because it saves me a lot of time resizing the table while the keyboard pops up when user presses the text field. In fact I've never been able to reproduce this animation using UIViewController and listening to textfields' delegate. It was almost perfect but there were some visible disadvantages comparing to the behaviour of table if displayed using UITableViewController.
So everything's fine when there are only textfields. But what about the date fileds? I want to make it exactly like the Contacts.app by Apple when I want to add a new contact and specify the birthday. In that application the Date Picker is shown, the table is resized, switching between email/phone field and birthday works great. I could believe that the date picker is in this case the keyboard but not for typing phone/email but date because it slides in/out just like a keyboard and is replaced instantly when the keyboard/picker is opened.
How did thet accomplished it?
Or where can I find the easiest solution to reproduce it. I believe it cannot be as hard because it's very common situation.
Regards
Chris
All of that is pointless. We should deal with the inputView and inputAccessoryView, where inputView should has the picker and inputAccessoryView the toolbar.
You're going to have to create a UIWindow object, then add a view. The windowLevel property makes it higher than the statusBar, which you may or may not want.
//statusWindow is a UIWindow ivar declared in the header
//pickerShowing is declared as a BOOL in header
//release and removeFromSuperview is done in the animation delegate methods
//ANIMATE IN
-(void)slideIn {
CGRect pickerFrame = CGRectMake(0.0f, 0.0f, 320.0f, 200.0f); //guessing on height
UIView *viewForPicker = [[UIView alloc] init];
UIPickerView *aPicker = [[UIPickerView alloc] init]; //don't forget to set delegate and dataSource
viewForPicker.frame = pickerFrame;
statusWindow = [[UIWindow alloc] initWithFrame:CGRectMake(0.0, 480.0, 320.0f, 200.0f)];//guessing on height, y value is off the screen (bottom)
statusWindow.windowLevel = UIWindowLevelStatusBar;
statusWindow.hidden = NO;
statusWindow.backgroundColor = [UIColor clearColor];
[statusWindow makeKeyAndVisible];
[viewForPicker addSubview:aPicker];
[statusWindow addSubview:viewForPicker];
[viewForPicker release];
[aPicker release];
[UIView beginAnimations:#"slideUp" context:nil];
[UIView setAnimationDuration:0.3];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationFinished:)];
statusWindow.frame = CGRectMake(0.0f, 200.0f, 320.0f, 200.0f); //guessing on y and height values, change them to suit needs
[UIView commitAnimations];
pickerShowing = YES;
}
//ANIMATE out:
-(void)slideOut {
[UIView beginAnimations:#"slideDown" context:nil];
[UIView setAnimationDuration:0.3];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationFinished:)];
statusWindow.frame = CGRectMake(0.0f, 480.0f, 320.0f, 200.0f);
[UIView commitAnimations];
pickerShowing = NO;
}
-(void)animationFinished:(NSString *)name {
if ([name isEqualToString:#"slideDown"]) {
[statusWindow release];
}
}
If you want to slide in/out the picker view, you can use Core Animation.
Simplest snippet:
// Slide picker view in
[UIView beginAnimations: #"SlideIn" context: nil];
myPickerView.frame = upFrame;
[UIView commitAnimations];
// ...
// Slide picker view out
[UIView beginAnimations: #"SlideOut" context: nil];
myPickerView.frame = downFrame;
[UIView commitAnimations];
upFrame and downFrame are CGRect you make with the right position for your picker view on screen and off screen respectively.
Hope this helps.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *targetCell = [tableView cellForRowAtIndexPath:indexPath];
self.pickerView.date = [self.dateFormatter dateFromString:targetCell.detailTextLabel.text];
// check if our date picker is already on screen
if (self.pickerView.superview == nil)
{
[self.view.window addSubview: self.pickerView];
// size up the picker view to our screen and compute the start/end frame origin for our slide up animation
//
// compute the start frame
CGRect screenRect = [[UIScreen mainScreen] applicationFrame];
CGSize pickerSize = [self.pickerView sizeThatFits:CGSizeZero];
CGRect startRect = CGRectMake(0.0,
screenRect.origin.y + screenRect.size.height,
pickerSize.width, pickerSize.height);
self.pickerView.frame = startRect;
// compute the end frame
CGRect pickerRect = CGRectMake(0.0,
screenRect.origin.y + screenRect.size.height - pickerSize.height,
pickerSize.width,
pickerSize.height);
// start the slide up animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
// we need to perform some post operations after the animation is complete
[UIView setAnimationDelegate:self];
self.pickerView.frame = pickerRect;
// shrink the table vertical size to make room for the date picker
CGRect newFrame = self.tableView.frame;
newFrame.size.height -= self.pickerView.frame.size.height;
self.tableView.frame = newFrame;
[UIView commitAnimations];
// add the "Done" button to the nav bar
self.navigationItem.rightBarButtonItem = self.doneButton;
}
}
- (void)slideDownDidStop
{
// the date picker has finished sliding downwards, so remove it
[self.pickerView removeFromSuperview];
}
- (IBAction)dateAction:(id)sender
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
cell.detailTextLabel.text = [self.dateFormatter stringFromDate:self.pickerView.date];
}
- (IBAction)doneAction:(id)sender
{
CGRect screenRect = [[UIScreen mainScreen] applicationFrame];
CGRect endFrame = self.pickerView.frame;
endFrame.origin.y = screenRect.origin.y + screenRect.size.height;
// start the slide down animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
// we need to perform some post operations after the animation is complete
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(slideDownDidStop)];
self.pickerView.frame = endFrame;
[UIView commitAnimations];
// grow the table back again in vertical size to make room for the date picker
CGRect newFrame = self.tableView.frame;
newFrame.size.height += self.pickerView.frame.size.height;
self.tableView.frame = newFrame;
// remove the "Done" button in the nav bar
self.navigationItem.rightBarButtonItem = nil;
// deselect the current table row
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
You can download a full working sample app from Apple demonstrating just this.
http://developer.apple.com/library/ios/#samplecode/DateCell/Introduction/Intro.html

Hiding keyboard with UIScrollView without glitches

I have multiple editable textfiels and some of them are covered with keyboard. So I used UIScrollView and it works quite nice.
Problem is when I want to hide keyboard. If I was scrolled down, after the keyboard hides, everything jumps up as it was at beginning (without keyboard). I want to tween this part as the keyboard is hiding.
So far I got this code (2 methods for keyboard events):
-(void)keyboardWillShow:(NSNotification *)notif{
if(keyboardVisible)
return;
keyboardVisible = YES;
NSDictionary* info = [notif userInfo];
NSValue* value = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [value CGRectValue].size;
CGRect viewFrame = self.view.frame;
viewFrame.size.height -= keyboardSize.height;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.3];
[scrollView setFrame:viewFrame];
[UIView commitAnimations];
}
- (void)keyboardWillHide:(NSNotification *)notif{
if(!keyboardVisible)
return;
keyboardVisible = NO;
NSDictionary* info = [notif userInfo];
NSValue* value = [info objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [value CGRectValue].size;
CGRect viewFrame = self.view.frame;
viewFrame.size.height += keyboardSize.height;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.3];
[scrollView setFrame:viewFrame];
[UIView commitAnimations];
}
It works pretty well with hiding keyboard but unfortunately it doesn't work when user switches from one text field to another. It will fire keyboardWillHide and keyboardWillShow events, one right after another. This will result in two animations, second one interrupting the first one. It doesn't look good.
Problem is with keyboardWillHide firing even when keyboard will not hide. At that point I don't know if keyboard will be shown again or not.
I also tried it with UIScrollView scrollRectToVisible and setContentOffset methods.. but they resulted in glitches when keyboard was hiding.
use this method to handle multiple text field and keyboard
-(void)scrollViewToCenterOfScreen:(UIView *)theView
{
CGFloat viewCenterY = theView.center.y;
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
CGFloat availableHeight = applicationFrame.size.height - 200; // Remove area covered by keyboard  
CGFloat y = viewCenterY - availableHeight / 2.0;
if (y < 0) {
y = 0;
}
[scrollview setContentOffset:CGPointMake(0, y) animated:YES];
}
call it in textfield delegate=
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self scrollViewToCenterOfScreen:textField];
}
and set scroll view frame in the below textfield delegate=
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
[self.scrollview setContentOffset:CGPointMake(0, 0) animated:YES];
return YES;
}
Why not use boolean values to indicate whether it is an appearance or just changing?

UITextView and UIPickerView with its own UIToolbar

I like to replicate the form behavior of Safari on the iPhone in my own app. If you enter data in an web form you get a separate UIToolbar (previous, next, done) just above the UIKeyboardView. Same for choosing an option: you get the same UIToolbar just above an UIPickerView.
I am looking for demos / sourcode / ideas how to implement this. Would I create my own subview with that toolbar and textview / pickerview? Is there a more elegant way? Especially something that leverages becomeFirstResponder of UITextfield?
So i created a UIViewCOntroller subclass to manage this.
on that i wrote this function to add.
-(void) addToViewWithAnimation:(UIView *) theView
{
UIView* myview = self.view;
CGRect frame = myview.frame;
frame.origin.y = 420;
myview.frame = frame;
UIView* bgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 420)];
bgView.backgroundColor = [UIColor blackColor];
bgView.alpha = 0.6;
backgroundView = bgView;
[theView addSubview: bgView]; // this adds in the dark background
[theView addSubview:self.view]; // this adds in the pickerView with toolbar.
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
frame = myview.frame;
frame.origin.y = 420 - frame.size.height;
myview.frame = frame;
[UIView commitAnimations];
}
I then created the view in IB, here is what my class Header looked like at the end of that. (there is also a UItoolbar on the view i just do not have a reference to it in my Controller)
#interface PropertyPickerController : UIViewController {
IBOutlet UIPickerView* Picker;
IBOutlet UIButton* DoneButton;
IBOutlet UIButton* CancelButton;
UIView* backgroundView;
NSArray* SimpleObjects;
id PickerObjectDelegate;
SEL PickerObjectSelector;
}
To then hide the view i use.
-(void) removeFromSuperviewWithAnimation
{
UIView* myview = self.view;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(AnimationDidStop:)];
[UIView setAnimationDuration:0.5];
// set fram below window.
CGRect frame = myview.frame;
frame.origin.y = 420;
myview.frame = frame;
backgroundView.alpha = 0; //fades shade to nothing
[UIView commitAnimations];
}
-(void) AnimationDidStop:(id) object
{
[self.view removeFromSuperview]; //removes view after animations.
[backgroundView removeFromSuperview];
}
And last but not least all the delegate functions for the picker.
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
FBSimpleObject* object = (FBSimpleObject*)[SimpleObjects objectAtIndex:row];
return object.Name;
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{ return 1;}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return [SimpleObjects count];
}
- (IBAction)CancelButtonClick
{
[self removeFromSuperviewWithAnimation];
}
- (IBAction)DoneButtonClick
{
//This performs a selector when the done button is clicked, makes the controller more versatile.
if(PickerObjectDelegate && PickerObjectSelector)
{
NSMethodSignature* signature = [PickerObjectDelegate methodSignatureForSelector:PickerObjectSelector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:PickerObjectDelegate];
[invocation setSelector:PickerObjectSelector];
[invocation setArgument:&object atIndex:2];
[invocation retainArguments];
[invocation invoke];
}
}
This is how you do the ToolBar. Basically i use the same concept with a ViewController subclass, and i dont use the standard push view or modal display options. (the example here actually places a Textbox and a toolbar on top of the keyboard.
#interface BugEditCommentController : UIViewController {
UITextView* Comment;
UIToolbar* Toolbar;
}
-(void) addToViewWithAnimation:(UIView*) theView;
To activate this view usually you would call [object becomeFirstResponder];
so if you add this to your view Controller constructor, all you need to do is call [object becomeFirstResponder];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(keyboardWillShow:) name: UIKeyboardWillShowNotification object:nil];
[nc addObserver:self selector:#selector(keyboardWillHide:) name: UIKeyboardWillHideNotification object:nil];
abd if you implement this method on your controller (defined in the above code)
-(void) keyboardWillShow:(NSNotification *) note
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
CGRect toolbarFrame = Toolbar.frame;
CGRect keyboardFrame;
CGPoint keyboardCenter;
[[note.userInfo valueForKey:UIKeyboardCenterEndUserInfoKey] getValue:&keyboardCenter];
[[note.userInfo valueForKey:UIKeyboardBoundsUserInfoKey] getValue: &keyboardFrame];
//CGRect toolbarRect = Toolbar.center;
toolbarFrame.origin.y= keyboardCenter.y - ((keyboardFrame.size.height/2) + (toolbarFrame.size.height));
Toolbar.frame = toolbarFrame;
[UIView commitAnimations];
}
-(void) keyboardWillHide:(id) object
{
//you could call [self removeFromSuperviewHere];
}
-(void) removeFromsuperViewWithAnimation
{
[Comment resignFirstResponder];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(AnimationDidStop:)];
CGRect frame = Toolbar.frame;
frame.origin.y = 480;
Toolbar.frame = frame;
[self.view viewWithTag:1].alpha = 0; //fade transparent black background to clear.
[UIView commitAnimations];
}
-(void)AnimationDidStop:(id) object
{
[self.view removeFromSuperview];
}
hope the additional info helps.
I'm looking for the solution for this issue too.
I found this was the best solution, you can use this SCKit to add tool bar to dismiss the UIPickerView or the UIDatePicker as you want.
Following is github link: https://github.com/scelis/SCKit/tree/
Have fun!

How to resize a tableHeaderView of a UITableView?

I'm having trouble resizing a tableHeaderView. It simple doesn't work.
1) Create a UITableView and UIView (100 x 320 px);
2) Set the UIView as tableHeaderView of the UITableView;
3) Build and Go. Everything is ok.
Now, I want to resizing the tableHeaderView, so I add this code in viewDidLoad:
self.tableView.autoresizesSubviews = YES;
self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;
CGRect newFrame = self.tableView.tableHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
self.tableView.tableHeaderView.frame = newFrame;
The height of the tableHeaderView should appear with 200, but appears with 100.
If I write:
self.tableView.autoresizesSubviews = YES;
CGRect newFrame = myHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
myHeaderView.frame = newFrame;
self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;
Then it starts with 200 of height, as I want. But I want to be able to modify it in runtime.
I've also tried this, without success:
self.tableView.autoresizesSubviews = YES;
self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;
CGRect newFrame = self.tableView.tableHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
self.tableView.tableHeaderView.frame = newFrame;
[self.tableView.tableHeaderView setNeedsLayout];
[self.tableView.tableHeaderView setNeedsDisplay];
[self.tableView setNeedsLayout];
[self.tableView setNeedsDisplay];
The point here is: How do we resize a tableHeaderView in runtime ???
Have anyone able to do this?
Thanks
iMe
FYI: I've gotten this to work by modifying the tableHeaderView and re-setting it. In this case, i'm adjusting the size of the tableHeaderView when the UIWebView subview has finished loading.
[webView sizeToFit];
CGRect newFrame = headerView.frame;
newFrame.size.height = newFrame.size.height + webView.frame.size.height;
headerView.frame = newFrame;
[self.tableView setTableHeaderView:headerView];
This answer is old and apparently doesn't work on iOS 7 and above.
I ran into the same problem, and I also wanted the changes to animate, so I made a subclass of UIView for my header view and added these methods:
- (void)adjustTableHeaderHeight:(NSUInteger)newHeight{
NSUInteger oldHeight = self.frame.size.height;
NSInteger originChange = oldHeight - newHeight;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0f];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
self.frame = CGRectMake(self.frame.origin.x,
self.frame.origin.y,
self.frame.size.width,
newHeight);
for (UIView *view in [(UITableView *)self.superview subviews]) {
if ([view isKindOfClass:[self class]]) {
continue;
}
view.frame = CGRectMake(view.frame.origin.x,
view.frame.origin.y - originChange,
view.frame.size.width,
view.frame.size.height);
}
[UIView commitAnimations];
}
- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
[(UITableView *)self.superview setTableHeaderView:self];
}
This essentially animates all the subviews of the UITableView that aren't the same class type as the calling class. At the end of the animation, it calls setTableHeaderView on the superview (the UITableView) – without this the UITableView contents will jump back the next time the user scrolls. The only limitation I've found on this so far is if the user attempts to scroll the UITableView while the animation is taking place, the scrolling will animate as if the header view hasn't been resized (not a big deal if the animation is quick).
If you want to conditionally animate the changes you can do the following:
- (void) showHeader:(BOOL)show animated:(BOOL)animated{
CGRect closedFrame = CGRectMake(0, 0, self.view.frame.size.width, 0);
CGRect newFrame = show?self.initialFrame:closedFrame;
if(animated){
// The UIView animation block handles the animation of our header view
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
// beginUpdates and endUpdates trigger the animation of our cells
[self.tableView beginUpdates];
}
self.headerView.frame = newFrame;
[self.tableView setTableHeaderView:self.headerView];
if(animated){
[self.tableView endUpdates];
[UIView commitAnimations];
}
}
Please note that the animation is two-folded:
The animation of the cells below the tableHeaderView. This is done using beginUpdates and endUpdates
The animation of the actual header view. This is done using a UIView animation block.
In order to synchronize those two animations the animationCurve has to be set to UIViewAnimationCurveEaseInOut and the duration to 0.3, which seems to be what the UITableView uses for it's animation.
Update
I created an Xcode project on gihub, which does this.
Check out the project ResizeTableHeaderViewAnimated in besi/ios-quickies
I think it should work if you just set the height of myHeaderView like so:
CGRect newFrame = myHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
myHeaderView.frame = newFrame;
self.tableView.tableHeaderView = myHeaderView;
Used #garrettmoon solution above until iOS 7.
Here's an updated solution based on #garrettmoon's:
- (void)adjustTableHeaderHeight:(NSUInteger)newHeight animated:(BOOL)animated {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:[CATransaction animationDuration]];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
self.frame = CGRectMake(self.frame.origin.x,
self.frame.origin.y,
self.frame.size.width,
newHeight);
[(UITableView *)self.superview setTableHeaderView:self];
[UIView commitAnimations];
}
- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
[(UITableView *)self.superview setTableHeaderView:self];
}
This worked for me on iOS 7 and 8. This code is running on the table view controller.
[UIView animateWithDuration:0.3 animations:^{
CGRect oldFrame = self.headerView.frame;
self.headerView.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, oldFrame.size.width, newHeight);
[self.tableView setTableHeaderView:self.headerView];
}];
Its because the setter of tableHeaderView.
You have to set the UIView height before set the tableHeaderView. (Would be much easier if Apple open sources this framework...)
On iOS 9 and below, tableHeaderView would not re-layout after resizing it.
This issue is resolved in iOS 10.
To solve this issue, just do it with the following code:
self.tableView.tableHeaderView = self.tableView.tableHeaderView;
On iOS 9.x, doing this on viewDidLoad works just fine:
var frame = headerView.frame
frame.size.height = 11 // New size
headerView.frame = frame
headerView is declared as #IBOutlet var headerView: UIView! and connected on the storyboard, where it is placed at the top of the tableView, to function as the tableHeaderView.
This is only for when you use auto-layout and set translatesAutoresizingMaskIntoConstraints = false to a custom header view.
The best and the simplest way is to override intrinsicContentSize. Internally UITableView uses intrinsicContentSize to decide its header/footer size. Once you have override intrinsicContentSize in your custom view, What you need to do is as below
configure the custom header/footer view's layout(subviews)
invoke invalidateIntrinsicContentSize()
invoke tableView.setNeedsLayout() and tableView.layoutIfNeeded()
Then the UITableView's header/footer will be updated as you want. No need to set the view nil or reset.
One thing really interesting for the UITableView.tableHeaderView or .tableFooterView is that UIStackView loose its ability to manage its arrangedSubviews. If you want to use UIStackView as a tableHeaderView or tableFooterView, you have to embed the stackView in a UIView and override UIView's intrinsicContentSize.
For swift 5 Tested code
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard let headerView = self.tblProfile.tableHeaderView else {
return
}
let size = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
if headerView.frame.size.height != size.height {
headerView.frame.size.height = size.height
self.tblProfile.tableHeaderView = headerView
self.tblProfile.layoutIfNeeded()
}
}
Note : You need to give all subview's constraints form top, bottom, leading, trailing. So it will get whole required size.
Reference taken from : https://useyourloaf.com/blog/variable-height-table-view-header/
Setting the height for header view property tableView.tableHeaderView in viewDidLoad seems not work, the header view height still not change as expected.
After fighting against this issue for many tries. I found that, you can change the height by invoking the header view create logic inside the
- (void)didMoveToParentViewController:(UIViewController *)parent method.
So the example code would look like this:
- (void)didMoveToParentViewController:(UIViewController *)parent {
[super didMoveToParentViewController:parent];
if ( _tableView.tableHeaderView == nil ) {
UIView *header = [[[UINib nibWithNibName:#"your header view" bundle:nil] instantiateWithOwner:self options:nil] firstObject];
header.frame = CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), HeaderViewHeight);
[_tableView setTableHeaderView:header];
}
}
If custom headerView is designed using autolayout and headerView needs to be updated after web-fetch or similar lazy task.
then in iOS-Swift I did this and got my headerView updated using bellow code:
//to reload your cell data
self.tableView.reloadData()
DispatchQueue.main.async {
// this is needed to update a specific tableview's headerview layout on main queue otherwise it's won't update perfectly cause reloaddata() is called
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
I found the initWithFrame initializer of a UIView doesn't properly honor the rect I pass in. Hence, I did the following which worked perfectly:
- (id)initWithFrame:(CGRect)aRect {
CGRect frame = [[UIScreen mainScreen] applicationFrame];
if ((self = [super initWithFrame:CGRectZero])) {
// Ugly initialization behavior - initWithFrame will not properly honor the frame we pass
self.frame = CGRectMake(0, 0, frame.size.width, 200);
// ...
}
}
The advantage of this is it is better encapsulated into your view code.
I have implemented animated height change of the table's header to expand to overall screen when tapped. However, the code can help in other cases:
// Swift
#IBAction func tapped(sender: UITapGestureRecognizer) {
self.tableView.beginUpdates() // Required to update cells.
// Collapse table header to original height
if isHeaderExpandedToFullScreen {
UIView.animateWithDuration(0.5, animations: { () -> Void in
self.scrollView.frame.size.height = 110 // original height in my case is 110
})
}
// Expand table header to overall screen
else {
let screenSize = self.view.frame // "screen" size
UIView.animateWithDuration(0.5, animations: { () -> Void in
self.scrollView.frame.size.height = screenSize.height
})
}
self.tableView.endUpdates() // Required to update cells.
isHeaderExpandedToFullScreen= !isHeaderExpandedToFullScreen // Toggle
}
UITableView resizing header - UISearchBar with Scope Bar
I wanted a UITableView with a UISearchBar as the header to the table so I have a hierarchy that looks like this
UITableView
|
|--> UIView
| |--> UISearchBar
|
|--> UITableViewCells
UISearchBarDelegate methods
As has been stated elsewhere, if you don't setTableViewHeader after changing it, nothing will happen.
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
{
searchBar.showsScopeBar = YES;
[UIView animateWithDuration:0.2f animations:^{
[searchBar sizeToFit];
CGFloat height = CGRectGetHeight(searchBar.frame);
CGRect frame = self.tableView.tableHeaderView.frame;
frame.size.height = height;
self.tableHeaderView.frame = frame;
self.tableView.tableHeaderView = self.tableHeaderView;
}];
[searchBar setShowsCancelButton:YES animated:YES];
return YES;
}
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
{
searchBar.showsScopeBar = NO;
[UIView animateWithDuration:0.f animations:^{
[searchBar sizeToFit];
CGFloat height = CGRectGetHeight(searchBar.frame);
CGRect frame = self.tableView.tableHeaderView.frame;
frame.size.height = height;
self.tableHeaderView.frame = frame;
self.tableView.tableHeaderView = self.tableHeaderView;
}];
[searchBar setShowsCancelButton:NO animated:YES];
return YES;
}
Obviously, by now Apple should have implemented UITableViewAutomaticDimension for tableHeaderView & tableFooterView...
The following seems to work for me using layout contraint(s):
CGSize s = [ self systemLayoutSizeFittingSize : UILayoutFittingCompressedSize ];
CGRect f = [ self frame ];
f.size = s;
[ self setFrame : f ];
If your tableHeaderView is a content adjustable webView,you can try:
[self.webView.scrollView addObserver:self forKeyPath:#"contentSize" options:NSKeyValueObservingOptionNew context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
self.webView.height = self.webView.scrollView.contentSize.height;
self.tableView.tableHeaderView = self.webView;
}
I tested it on iOS9 and iOS11,worked well.
Did you try
[self.tableView reloadData] after changing the height?