I am trying to put animation in a UITableViewCell. Animation is that onClick table view cell change the frame of tableCell into tableview frame.
I have the following code:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 10;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell*cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell*cell = [tableView cellForRowAtIndexPath:indexPath];
if(cell.frame.size.height == tableView.bounds.size.height){
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}else{
cell.frame = tableView.bounds;
[[cell superview] bringSubviewToFront:cell];
}
[UIView animateWithDuration:0.5 delay:0.0 usingSpringWithDamping:0.5 initialSpringVelocity:0.2 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
}];
}
It works fine but after change cell's frame I am able to tap another cells also which I don't want. How can I achieve this?
One of the possible solution would be to declare a variable that will hold an array of indexPath of expanded cell like this
// Will hold the indexPath of expanded cell
var expandedCell : [IndexPath] = []
Second thing would be adding and removing the the cell that are/aren't expanded and to do that you have to update your UITableView Delegate didSelectRowAt
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let index = expandedCell.firstIndex { (localIndexPath) -> Bool in
return indexPath == localIndexPath
}
if let index = index {
expandedCell.remove(at: index)
} else {
expandedCell.append(indexPath)
}
tableviewMessageList.reloadRows(at: [indexPath], with: .fade)
}
And in the end you have to add another UITableView Delegate heightForRowAt to return the height of the cell if the cell's indexPath is in array it will return the expanded size else return the normal size of your cell like this:-
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if expandedCell.contains(indexPath) {
return tableView.frame.height
}
return 200.0 //NormalHeight
}
Note: My answer is in Swift but the same principle will apply for Objective-C you just need to change the Syntax.
Overall, I think a better approach to this would be to use a separate view to display the cell details full screen, although you may have some restrictions that require you to do it this way. That being said, find my answer below.
This is what I gather your current problem is:
You'd like to show the cell expanded, and when in that expanded state touches are going through the cell view and hitting the table view behind it and re-triggering didSelectRowAtIndexPath: tableview delegate method (on other cells than the expanded one).
These are a couple of the possible solutions I see:
Add a tap gesture recognizer to the cell when it's expanded so it will absorb the touch, then remove the gesture recognizer once it's consumed.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTapOnExpandedCell:)];
[cell addGestureRecognizer:tap];
cell.frame = tableView.bounds;
[[cell superview] bringSubviewToFront:cell];
UIView *bgView = [[UIView alloc] init];
bgView.backgroundColor = [UIColor purpleColor];
cell.selectedBackgroundView = bgView;
[UIView animateWithDuration:0.5 delay:0.0 usingSpringWithDamping:0.5 initialSpringVelocity:0.2 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.view layoutIfNeeded];
} completion:nil];
}
-(void)didTapOnExpandedCell:(UIGestureRecognizer *)recognizer {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:self.tableView.indexPathForSelectedRow];
// do whatever you were planning on doing when tapping on the
// expanded cell
[self.tableView reloadRowsAtIndexPaths:#[self.tableView.indexPathForSelectedRow] withRowAnimation:UITableViewRowAnimationNone];
[cell removeGestureRecognizer:recognizer];
}
Subclass UITableViewCell and override touchesBegan:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
R4NTableViewCell *cell = (R4NTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
cell.frame = tableView.bounds;
[[cell superview] bringSubviewToFront:cell];
UIView *bgView = [[UIView alloc] init];
bgView.backgroundColor = [UIColor purpleColor];
cell.selectedBackgroundView = bgView;
[UIView animateWithDuration:0.5 delay:0.0 usingSpringWithDamping:0.5 initialSpringVelocity:0.2 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.view layoutIfNeeded];
} completion:nil];
}
// in R4NTableViewCell.m implementation override touchesBegan
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// if we're not selected, don't intercept the touch so the tableview can handle it
// calling back to super will give the default tableview behavior and get our delegate callback
if (self.selected == NO) {
self.frameBeforeExpansion = self.frame;
[super touchesBegan:touches withEvent:event];
} else { // we're in the expanded state so intercept the touch
NSSet <UITouch *> *singleTouches = [[event allTouches] objectsPassingTest:^BOOL(UITouch * _Nonnull obj, BOOL * _Nonnull stop) {
return obj.tapCount == 1;
}];
if (singleTouches.count > 0) {
// the user single tapped our view
[UIView animateWithDuration:1.0 delay:0.0 usingSpringWithDamping:0.7 initialSpringVelocity:0.2 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.frame = self.frameBeforeExpansion;
[self.superview layoutIfNeeded];
} completion:^(BOOL finished) {
[self setSelected:NO];
self.backgroundView.backgroundColor = [UIColor greenColor];
}];
}
}
}
One additional thing I didn't really understand from your explanation (as the comments mentioned) is what you expect to happen when the user taps the expanded tableview cell.
Related
How top expand collapse custom cell tableview and expanding the cell must be dynamic height in iOS objective c?
viewcontroller having custom tableview cells when click on the cell,cell must be expand/collapse having one view and display data in that view and height must be dynamic
You'll have to maintain a state (expanded/not expanded) for the custom table view cell. Every time it's tapped you make the necessary changes in the UI and toggle the state. You can then call
reloadData()
or
-(void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
withRowAnimation:(UITableViewRowAnimation)animation;
You can also calculate and set the new height of the cell and the reload function will take care of the animation.
You used tableview then easy manage expand and collapse custom cell tableview and expanding with animation.
Follow code for expand and collapse.
.h File
BOOL currentlyExpanded;
NSMutableIndexSet *expandedSections;
.m File
- (void)viewDidLoad
{
if (!expandedSections)
{
expandedSections = [[NSMutableIndexSet alloc] init];
}
}
Use Tableview Delegate and Datasource method..
#pragma mark - Tableview Delegate -
- (BOOL)tableView:(UITableView *)tableView canCollapseSection:(NSInteger)section
{
return YES;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([self tableView:tableView canCollapseSection:section])
{
if ([expandedSections containsIndex:section])
return 6; //return your array count..
else
return 1;
}
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier]
;
}
if ([self tableView:tableView canCollapseSection:indexPath.section])
{
//Display your dynamic cell contain..
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self tableView:tableView canCollapseSection:indexPath.section])
{
NSInteger section = indexPath.section;
currentlyExpanded = [expandedSections containsIndex:section];
NSInteger rows;
if (currentlyExpanded)
{
rows = [self tableView:tableView numberOfRowsInSection:section];
[expandedSections removeIndex:section];
}
else
{
[expandedSections addIndex:section];
rows = [self tableView:tableView numberOfRowsInSection:section];
}
// Animation when cell expand and collapse
if(currentlyExpanded)
{
[tableView deleteRowsAtIndexPaths:tmpArray
withRowAnimation:UITableViewRowAnimationTop];
CGFloat startRadians, endRadians;
startRadians = M_PI/2;
endRadians = 0.0f;
btnArrow.layer.affineTransform = CGAffineTransformMakeRotation(startRadians);
[UIView animateWithDuration:0.3f animations:^{
btnArrow.layer.affineTransform = CGAffineTransformMakeRotation(endRadians);
}completion:^(BOOL finished){
}];
}
else
{
[tableView insertRowsAtIndexPaths:tmpArray
withRowAnimation:UITableViewRowAnimationTop];
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
[btnArrow setTransform:CGAffineTransformRotate(btnArrow.transform, M_PI/2 )];
}completion:^(BOOL finished){
}];
}
}
}
Im trying to create a cool animation with my collection view selection. Basically I have a collection view of photo cells displayed. What I want to happen is have the cell pop out of its location, scale and move to the center of the screen where the user will be prompted to confirm the selection.
Here is my code so far, but currently the animation takes the cells original frame location, so if I scroll it doesnt take into account that the frame position is no longer the same.
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
PhotoCell *cell = (PhotoCell *)[collectionView cellForItemAtIndexPath:indexPath];
collectionView.allowsSelection = NO;
[self createAnimationWithCell:cell];
}
- (void)createAnimationWithCell:(PhotoCell *)cell {
UIImageView *selectedImage = [[UIImageView alloc] initWithFrame:cell.bounds];
selectedImage.center = cell.center;
selectedImage.image = cell.imageView.image;
[self.view addSubview:selectedImage];
[UIView animateWithDuration:2.5 animations:^{
selectedImage.center = self.view.center;
} completion:^(BOOL finished) {
[selectedImage removeFromSuperview];
self.collectionView.allowsSelection = YES;
}];
}
I solved it myself:
For those who are experiencing the same problem, we have to take into account how much the collection view has scrolled (offset) and use that information. I created a variable called collectionViewOffset and gave it an initial value of 0.0
then used:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// getting the scroll offset
collectionViewOffset = scrollView.contentOffset.y;
NSLog(#"offset: %f", collectionViewOffset);
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
PhotoCell *cell = (PhotoCell *)[collectionView cellForItemAtIndexPath:indexPath];
//collectionView.allowsSelection = NO;
//collectionView.scrollEnabled = NO;
[cell.superview bringSubviewToFront:cell];
[UIView animateWithDuration:2.0 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:.2 options:UIViewAnimationOptionCurveLinear animations:^{
cell.center = CGPointMake(self.view.vWidth/2, self.bannerView.vBottomEdge + collectionViewOffset + 40);
} completion:^(BOOL finished) {
}];
}
I have image view on my custom cell. All elements on cell have constraints. I have implement this method for animation my arrow. Rotation works good but without animation. I have debug cell and awake from nib method was invoked.
I have header that I built from the storyboard cell:
View controller callback:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
static NSString *cellIdentifier = #"SectionHeader";
KNCategorySectionCell *sectionView = (KNCategorySectionCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
sectionView.index = section;
sectionView.delegate = self;
KNCategoryPoxy *category = [_categories objectAtIndex:section];
[sectionView setupViewWithState:category.state];
sectionView.categoryName.text = category.dictionary[#"name"];
return sectionView;
}
Custom cell implementation of method:
- (void)setupViewWithState:(KNCategoryState)state
{
switch (state)
{
case KNCategoryStateStateCollapsed:
{
[self.contentView setBackgroundColor:[UIColor red:255 green:255 blue:255]];
} break;
case KNCategoryStateStateExpanded:
{
[UIView animateWithDuration:10.0f delay:0.0f options:UIViewAnimationOptionCurveEaseOut animations:^{
self.arrowImage.transform = CGAffineTransformMakeRotation(M_PI_2);
} completion:^(BOOL finished) {
}];
[self.contentView setBackgroundColor:[UIColor red:230 green:230 blue:230]];
} break;
}
}
As I said the rotation is worked but it instantly rotate view to appropriate M_PI_2 and seems there is no animation.
I have used Xcode 6.1 and iOS 8.1 simulator for testing.
As additional I have implemented single tap gesture in custom cell and implemented this method:
- (void)singleTap
{
[self.delegate sectionDidTappedWithIndex:self.index];
}
delegate it is my view controller which contains table that display all custom cells.
In view controller I have implemented method that set header expanded and reload data:
- (void)sectionDidTappedWithIndex:(NSInteger)index
{
for (KNCategoryPoxy *category in _categories)
{
category.state = KNCategoryStateStateCollapsed;
}
KNCategoryPoxy *category = [_categories objectAtIndex:index];
category.state = !category.state;
[self.theTableView reloadData];
}
Try perform animations using UIView.commmitAnimations() eg:
UIView.beginAnimations(“rotation”, context: nil)
UIView.setAnimationDuration(0.8)
cell.layer.transform = CATransform3DIdentity
cell.alpha = 1
cell.layer.shadowOffset = CGSize(width: CGFloat(0), height: CGFloat(0))
UIView.commitAnimations()
also try to perform the operation inside willDisplayCell: Method.
Refer this http://www.thinkandbuild.it/animating-uitableview-cells/ solution to perform animations inside tableview cell
I would like to resize my tableView cell when the user selects a row, the cell becomes smaller then larger again. This is what I have tried so far:
#pragma mark UITableView Delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
// My animation
cell.contentView.transform = CGAffineTransformMakeScale(0.95,0.95);
cell.contentView.alpha = 1.f;
[UIView beginAnimations:#"button" context:nil];
[UIView setAnimationDuration:0.2];
cell.contentView.transform = CGAffineTransformMakeScale(1,1);
cell.contentView.alpha = 1.0f;
[UIView commitAnimations];
}
This is my tableView data source:
#pragma mark UITableView Datasource
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Set up cell
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
cell.backgroundColor = [UIColor clearColor];
cell.textLabel.font = [UIFont fontWithName:#"HelveticaNeue" size:21];
cell.textLabel.textColor = [UIColor whiteColor];
cell.textLabel.highlightedTextColor = [UIColor lightGrayColor];
cell.selectedBackgroundView = [[UIView alloc] init];
}
// Images in my cell
NSArray *images = #[#"Songs", #"Albums", #"Artists", #"Playlists"];
UIImageView *cellImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:images[indexPath.row]]];
cellImage.frame = CGRectMake(0, 0, 90, 90);
[cell addSubview:cellImage];
return cell;
}
But this does not work. The image in the cell simply stays the same size. Any ideas? Thanks.
I seriously advise going with a UICollectionView for something like this. With it, you only have to call performBatchUpdates and you can put any frame setting inside of it's block. It's practically magic!
I don't know if you can or should do what you ask about, but if you can it would probably be with a custom UITableViewCell. But, you might be able to fake it with a custom cell, maybe with a UIView that holds all the cell's contents. The UIView would animate inside the cell, and if you are not showing separators on the table it could look like the cell itself is animating. So basically you're hiding the actual cell, and using a UIView in the cell to simulate animating the cell. Maybe like this:
[UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.customCell.frame = CGRectMake(110, 20, 100, 20);
} completion:^(BOOL finished) {
[UIView animateWithDuration:SLIDE_TIME delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.customCell.frame = CGRectMake(0, 0, 320, 60);
} completion:^(BOOL finished) {
}];
}];
How do I create a good looking drop down menu in iOS. It should open when the user clicks a button in the navigation bar.
I tried creating a table, but got the error Static table views are only valid when embedded in UITableViewController instances. If I use UITableViewController, then it has to be full width.
Maybe you could try using a CollectionView instead of TableView, and if you need sopport for iOS 4+, PSTCollectionView is a good option :)
What I did is added a Button and on button tap one table view is shown, whenever any row is selected that row title is set to button title.
- (void)addOrganizationButton {
self.organizationButton = [UIUtils createButtonWithFrame:CGRectMake(203,115,451,38)
titleText:#"Organization"
type:UIButtonTypeCustom
normalImage:nil selectedImage:nil
target:self
view:self.registerView
tag:0
selector:#selector(organizationButtonTap)];
self.organizationButton.titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
[self.organizationButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
self.organizationButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
self.organizationButton.layer.borderWidth = 1;
}
//Show table view on button tap
- (void)organizationButtonTap {
self.organizationTable = [[UITableView alloc]initWithFrame:CGRectMake(203,153, 451,220)];
self.organizationTable.layer.borderWidth = 1;
self.organizationTable.dataSource = self;
self.organizationTable.delegate = self;
[UIView animateWithDuration:1
delay:0.0
options: UIViewAnimationOptionCurveEaseInOut
animations:^{
CGRect frame = self.organizationTable.frame;
frame.size.height = 220;
self.organizationTable.frame = frame;
}
completion:^(BOOL finished){
NSLog(#"Done!");
}];
[self.registerView addSubview:self.organizationTable];
}
//Table view delegates
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;//to be changed
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section{
return 3;//get dictionary count
}
- (UITableViewCell *) tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
NSString *cellIdentifier=#"Cell Identifier";
cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell==nil){
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
cell.accessoryType = UITableViewCellAccessoryNone;
}
cell.textLabel.text = [NSString stringWithFormat:
#"Section %ld, Cell %ld",
(long)indexPath.section,
(long)indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.organizationButton setTitle:#"faf" forState:UIControlStateNormal];
[UIView animateWithDuration:1
delay:0.0
options: UIViewAnimationOptionCurveEaseInOut
animations:^{
CGRect frame = self.organizationTable.frame;
frame.size.height = 0;
self.organizationTable.frame = frame;
}
completion:^(BOOL finished){
NSLog(#"Done!");
}];
}
You can prepare a custom view for that and add it as a subview in your ViewController.
Use UIView animations for simulating "drop down" effect.