I have a subclass of UITableViewController. I have code that can add/remove a UISearchBar to/from the tableHeaderView of my tableView. Here's the code I have to perform these tasks:
self.tableView.tableHeaderView = uiSearchBar; //Make the search bar appear
self.tableView.tableHeaderView = nil; //Make the search bar disappear
The problem is that I want the adding/removing of the UISearchBar to be animated; slide into view from the top when I add it then slide upwards and out of view when I remove it instead of just appearing and disappearing. Any suggestions?
Thanks.
I was finally able to figure this out. Turned out to be pretty simple. Instead of adding the UISearchBar to the table header, I insert it into the first cell of the table with animation UITableViewRowAnimationTop and remove it using the same method. This results in the bar sliding in and out from the top. Here is the code that makes the bar appear:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.baseUiTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationTop];
[self.baseUiTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
And here is the code that removes the search bar:
[uiSearchBar resignFirstResponder];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.baseUiTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationTop];
A much simpler way that is also suitable for UITableViewStyleGrouped (plus doesn't require changing the number of rows) is to animate the contentInset of the table:
CGFloat h = uiSearchBar.bounds.size.height;
UITableView *tv = self.tableView;
if (tv.tableHeaderView)
{ // hide bar
[UIView animateWithDuration:0.3 animations:^{
tv.contentInset = UIEdgeInsetsMake(-h, 0, 0, 0);
} completion:^(BOOL finished) {
tv.contentInset = UIEdgeInsetsZero;
tv.tableHeaderView = nil;
[tv scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
}];
}
else
{ // show bar
uiSearchBar.frame = CGRectMake(0, -h, tv.frame.size.width, h);
[UIView animateWithDuration:0.3 animations:^{
[tv scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
tv.tableHeaderView = uiSearchBar;
}];
}
I know this is three years after the OP asked the question, but since this is a good alternative to achieve the desired effect, I thought it might be useful for others meeting the same problem.
Try having your table view scroll—animated—so that only the row below the search bar is visible, then remove the search bar and scroll to the same row without animation.
Related
Based on the page change of a paged UIScrollView, I'm calling scrollToRowAtIndexPath:atScrollPosition:animated to the table specifics for that page that's in display.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
CGFloat pageWidth = self.scrollView.frame.size.width;
int page = floor((self.scrollView.contentOffset.x - pageWidth/2)/pageWidth)+1;
self.pageControl.currentPage = page;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:page];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
However, the animation of the automatic scrolling is happening too slow. Is there a way to make this animation more snappy?
A fast solution might be:
[UIView animateWithDuration:aSmallValue animations:^{
[self.tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop
animated:NO];
}];
How to update number of rows in
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
}
When it will expand increase number of rows.
`- (void) rotateButtonToExpanded:(UIButton*)aButton section:(int)aSection{
// Rotate the button with animation.
[UIView beginAnimations:ROTATE_TO_EXPAND context:nil];
[UIView setAnimationDuration:ANIMATION_DURATION];
aButton.transform = CGAffineTransformMakeRotation(M_PI*2.5);
[UIView commitAnimations];
// Save the state (expanded or collapsed) of button with index path as key.
BOOL isExpanded=![[mStateOfNodes objectAtIndex:aSection] isExpanded];
[[mStateOfNodes objectAtIndex:aSection] setIsExpanded:isExpanded];
NSArray*theDataOfChildCells=[[self.mNodes objectAtIndex:aSection] valueForKey:CHILD_NODES];
for (int i=0; i<theDataOfChildCells.count; i++) {
mNumberOfCells++;
[self beginUpdates];
NSIndexPath*theIndexPath=[NSIndexPath indexPathForRow:i inSection:aSection];
[self insertRowsAtIndexPaths:[NSArray arrayWithObject:theIndexPath] withRowAnimation:UITableViewRowAnimationMiddle];
[self endUpdates];
}
}`
used reload data methods,
[self.tableview reloadData];
Use Following code that use for add row in UITable with specific row and section
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowNO inSection:SectionNO];
[self.tblView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
rowNO=rowNO+1;
[self.tblView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
[self.tblView reloadData];
This may be a simple question for you experts but I can't figure out the correct solution.
In my app I created a a table view to list a no of items.
In the first row of the cell there a switch (ON/OFF).
So my client need to hide the bottom cells(other than zeroth cell) when switch is OFF and show all the cells when switch is ON.
I created the switch through code.
Can anyone please help me with how to do this?
Thanks in advance.
numberOfRows dataSource method - make it return 1 and reloadTableView
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
/// ....
self.switchView = [[[UISwitch alloc] initWithFrame:CGRectZero]autorelease];
cell.accessoryView = self.switchView;
[self.switchView setOn:NO animated:NO];
[self.switchView addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
//....
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if [self.switchView.on]
return 1;
else
{
// normal code return
}
}
- (void) switchChanged:(id)sender {
[self.tableView reloadData];
}
You could use insertRowsAtIndexPaths, this will not only add, but also do the animation of adding new row
first add target UIControlEventTouchUpInside into UISwitch.
[switch addTarget:self action:#selector(switchChange:) forControlEvents:UIControlEventValueChanged];
-(void) switchChange:(UISwitch*) sender{
isSwitchOn = sender.on; //edited
[myTable beginUpdates];
NSIndexPath *row1 = [NSIndexPath indexPathForRow:1 inSection:0];
NSIndexPath *row2 = [NSIndexPath indexPathForRow:2 inSection:0];
//you may add as many row as you want here.
[myTable insertRowsAtIndexPaths:[NSArray arrayWithObjects:row1,row2,nil] withRowAnimation:UITableViewRowAnimationMiddle];
[myTable endUpdates];
}
and make sure you will add how many rows you will insert in numberOfRowsInSection:
(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (isSwitchOn) return 1;
else
return 3; //how many rows would have after switched on
}
You might want to check [myTable insertSections:withRowAnimation: to insert another section and do the same as above. but remember to state how many section you will add, in numberOfSectionsInTableView:
UPDATE :
implement deleteRowsAtIndexPaths: if switch off.
-(void) switchChange:(UISwitch*) sender{
isSwitchOn = sender.on;
if(isSwitchOn){
[myTable beginUpdates];
NSIndexPath *row1 = [NSIndexPath indexPathForRow:1 inSection:0];
NSIndexPath *row2 = [NSIndexPath indexPathForRow:2 inSection:0];
//you may add as many row as you want here.
[myTable insertRowsAtIndexPaths:[NSArray arrayWithObjects:row1,row2,nil] withRowAnimation:UITableViewRowAnimationMiddle];
[myTable endUpdates];
}
else{
[myTable beginUpdates];
NSIndexPath *row1 = [NSIndexPath indexPathForRow:1 inSection:0];
NSIndexPath *row2 = [NSIndexPath indexPathForRow:2 inSection:0];
//you may add as many row as you want here.
[myTable deleteRowsAtIndexPaths:[NSArray arrayWithObjects:row1,row2,nil] withRowAnimation:UITableViewRowAnimationFade];
[myTable endUpdates];
}
}
There's a UITableViewDataSource method called tableView:numberOfRows:inSections method.Just check for the state of the switch and return 1 if it's OFF or all the rows if it is ON. Just call the [tableView reloadData] method each time you switch ON and OFF. As simple as that.
I want to do some operation in a particular cell using some function.
Here i used button in custom cell to call this function.
Each button has cell index value as title.
Using that title only, I can identify a particular cell to do some operation.
-(void)showInteractionView:(id)sender{
UIButton *btn=(UIButton *)sender;
lastSelInteractionVal=[btn.titleLabel.text intValue];
NSIndexPath *index=[NSIndexPath indexPathForRow:[btn.titleLabel.text intValue] inSection:0];
UITableViewCell *cell=[tblView cellForRowAtIndexPath:index];
UIView *v=(UIView *)[cell.contentView viewWithTag:INNERVIEW];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
v.frame=CGRectMake(0, 0, 320, 100);
[UIView commitAnimations];
}
But here I used custom cell so I want to assign tblView cell to a custom cell instead of this code below.
UITableViewCell *cell=[tblView cellForRowAtIndexPath:index];
Please give me a solution.
Thanks.
The same way you can get custom cell.
YourCustomCell *customCell = [tblView cellForRowAtIndexPath:index];
or you can do this
id Cell = [tblView cellForRowAtIndexPath:index];
if([cell isKindOfClass[yourCustomCellClassName class]]){
NSLog(#"Custom Cell Found");
}
I am having trouble animating a custom UITableView section header.
The goal was to create collapsable sections.
When I tap on the custom header the first time it animates as expected, however every time after that it leaves a duplicate in the original location and animates another.
Image Example:
My Custom Header:
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView* customView = [[[UIView alloc]initWithFrame:CGRectMake(10.0, 0.0, 300.0, 44.0)]autorelease];
customView.backgroundColor = [UIColor lightGrayColor];
UILabel * headerLabel = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.opaque = NO;
headerLabel.textColor = [UIColor darkGrayColor];
headerLabel.font = [UIFont boldSystemFontOfSize:16];
headerLabel.frame = CGRectMake(10, 7, 260.0, 44.0);
headerLabel.textAlignment = UITextAlignmentCenter;
NSDictionary *dictionary = [self.data objectAtIndex:section];
headerLabel.text = [dictionary objectForKey:#"Title"];
[customView addSubview:headerLabel];
// add button to right corner of section
UIButton* headerButton = [[UIButton alloc] initWithFrame:CGRectMake(10, 0, 320, 44)];
headerButton.center = CGPointMake( 160.0, 22.0);
headerButton.backgroundColor = [UIColor clearColor];
headerButton.tag = section;
[headerButton addTarget:self action:#selector(expandSection:) forControlEvents:UIControlEventTouchUpInside];
[customView addSubview:headerButton];
return customView;
}
My Animation Method:
- (void) expandSection:(id)sender {
if (expandedSection == [sender tag]) {
expandedSection = -1;
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
}else if (expandedSection == -1){
expandedSection = [sender tag];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
}else{
[self.tableView beginUpdates];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:expandedSection] withRowAnimation:UITableViewRowAnimationNone];
expandedSection = [sender tag];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
//[self.tableView reloadData];
}
I am not exactly sure whats going on, but the instances suggest that I need to dealoc something. I've tried a few things but I can't figure this out. Anyone help on this would be great!
Edit: I believe the problem is that reloadSections is causing the custom view to instance. I can't release the view because I need it as a reference to do the update for the animation. Any ideas on what I can do to fix this?
Solution found.
The table needed to be reloaded before every change. This way the table is at the latest state before making any changes.
add
[self.tableView reloadData];
as the fist entry in the "expandSection" method.
CODE:
- (void) expandSection:(id)sender {
[self.tableView reloadData];
if (expandedSection == [sender tag]) {
expandedSection = -1;
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
}else if (expandedSection == -1){
expandedSection = [sender tag];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
}else{
[self.tableView beginUpdates];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:expandedSection] withRowAnimation:UITableViewRowAnimationNone];
expandedSection = [sender tag];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
//[self.tableView reloadData];
}
I had a similar issue which was caused by using dynamic height cells. I had an expandable custom header view and when I was updating tableView to insert and remove the associated rows of the section (meaning that they were expanding, respectively collapsing), the section header which was a subclass of UITableViewHeaderFooterView was not recycled. So basically a new one was allocated and added over the old one resulting in overlapping views. The cell identifier was properly set so must have been something else. When I removed tableView.sectionHeaderHeight = UITableViewAutomaticDimension and implemented func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat the view got properly recycled and only one header view was displayed for each section.
Another solution that I turned out to actually work was to use UITableViewCell instead of UITableViewHeaderFooterView and when you return in func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? you just return cell.contenView, this will work because the method requires to return a UIView and since the contentView of the UITableViewCell is a UIView it works just fine. The idea behind is to use the recycling mechanism of UITableView through UITableViewCell and just return its content after you configure it.
Conclusion. The problem it is very possible to be caused by UITableViewHeaderFooterView when is used with self sizing tableView cells and UITableViewAutomaticDimension instead of manually calculating cell height.