So to make it simple I'm trying to have the same view as in iMessage: a reversed UITableView.
I have a rotated UITableView :
self.tableView.transform = CGAffineTransformMakeRotation(-M_PI);
Each UITableViewCell is also rotated to appear the right way:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] init];
cell.transform = CGAffineTransformMakeRotation(M_PI);
return cell;
}
When the keyboard appears, the frame of my UITableView is changed, so that the bottom of my UITableView follows the top of the keyboard. Same thing when the keyboard hides. To do this I use an animation.
My problem is that when the keyboards hide, the frame of the UITableView increases, and some new cells are displayed. As they are displayed, the delegate calls tableView:cellForRowAtIndexPath: and the animation also applies on the
cell.transform = CGAffineTransformMakeRotation(M_PI);
So I see my new cells rotating!
Is there any way I could avoid the animation on the rotation?
You need to disable animations if you don't want the setting of animatable properties to be animated:
BOOL wasEnabled = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
cell.transform = CGAffineTransformMakeRotation(M_PI);
[UIView setAnimationsEnabled:wasEnabled];
On iOS 7, you can use [UIView performWithoutAnimation:...].
Also, I would avoid doing
self.tableView.transform = CGAffineTransformMakeRotation(-M_PI);
Last I checked (iOS 5 or 6?), this would cause the cell sizes to be incorrect, as if UITableView used its frame's width to decide how "wide" cells should be. Stick it in a view and set the transform of that view instead (or check that it does the right thing on each major OS version you need to support).
Hey mate try to maintain a bool variable when you dont need the animation set the bool variable to false during some editing or any other event. So put the condition of bool variable if it is True then only go for animation.
regards and have a nice year ahead
Related
Currently I have a UITableView with a resizing UITextView in it. The cell is resizing automatically using beginUpdates/endUpdates, but when it does it the table view stutters (See the gif below).
The end result is a UITableViewCell that has a textview in it that resizes based on it's content. Here is the code within the custom UITableViewCell class that causes the UITableView to update itself.
- (void)textViewDidChange:(UITextView *)textView {
// This is a category on UITableViewCell to get the [self superView] as the UITableView
UITableView *tableView = [self tableView];
if (tableView){
[tableView beginUpdates];
[tableView endUpdates];
}
}
Here are the things that I have already tried:
Get the current contentOffset and resetting it after the endUpdates but didn't work
Disabling scrolling on the UITableView before updates and then enabling afterwards
I tried returning NO always from - (BOOL)textViewShouldEndEditing:(UITextView *)textView
My UITableView cell height is using UITableViewAutomaticDimension. Any other ideas or thoughts are welcome.
Here is a sample of what it looks like:
I am not looking to use any libraries so please no suggestions for that.
Thanks
Edit: Solution
Found Here: I do not want animation in the begin updates, end updates block for uitableview?
Credit to #JeffBowen for a great find (although hacky it is workable and allows me to still implement the UITableViewDelegate methods for supporting iOS 7). Turn animations off prior to performing update and then enable after update to prevent the UITableView from stuttering.
[UIView setAnimationsEnabled:NO];
[tableView beginUpdates];
[tableView endUpdates];
[UIView setAnimationsEnabled:YES];
If you don't need to use the Delegate methods and want a less hacky solution for iOS 8+ only then go with #Massmaker's answer below.
Just disable animation before calling beginUpdates and re-enable it after calling endUpdates.
[UIView setAnimationsEnabled:NO];
[tableView beginUpdates];
[tableView endUpdates];
[UIView setAnimationsEnabled:YES];
Definitely a hack but works for now. Credit to my friend Beau who pointed me to this.
My solution (for iOS 8) was first set in my viewController viewDidLoad
self.tableView.rowHeight = UITableViewAutomaticDimension;
// this line is needed to cell`s textview change cause resize the tableview`s cell
self.tableView.estimatedRowHeight = 50.0;
then, combining this article solution in Swift and some dirty thoughts
I`ve set in my cell a property, called
#property (nonatomic, strong) UITableView *hostTableView;
and in cell-s -(void) textViewDidChange:(UITextView *)textView
CGFloat currentTextViewHeight = _textContainer.bounds.size.height;
CGFloat toConstant = ceilf([_textContainer sizeThatFits:CGSizeMake(_textContainer.frame.size.width, FLT_MAX)].height);
if (toConstant > currentTextViewHeight)
{
[_hostTableView beginUpdates];
[_hostTableView endUpdates];
}
then in viewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
textCell.hostTableView = self.tableView;
I don't find a way to achieve it because:
When you trigger [tableView endUpdates], table recalculate contentSize and re-set it. And this cause resetting contentOffset to default value.
This is behaviour inherited from UIScrollView, and I tried to avoid it via:
Subclassing UITableView and overriding setContentOffset: and setContentOffset:animated functions. But they don't called when table view change contentSize.
Subclassing UITableView, overriding setContentSize: function and setting contentOffset to old value, after content size updating, but it not work for me
using KVO, and setting old value for contentOffset right after it reset, but anyway I have this animated issue
setting scrollEnabled to NO and scrollToTop to NO, but it also not help
If anybody find solution for this problem welcome.
Maybe possible solution - disable autolayout: iOS: setContentSize is causing my entire UIScrollView frame to jump
UPDATE: I find solution: direct changing cell height, content size and content offset:
This works for me (table view is delegate of cell's UITextView)
- (void)textViewDidChange:(UITextView *)textView
{
CGFloat textHeight = [textView sizeThatFits:CGSizeMake(self.width, MAXFLOAT)].height;
if (self.previousTextHeight != textHeight && self.previousTextHeight > 0) {
CGFloat difference = textHeight - self.previousTextHeight;
CGRect cellFrame = self.editedCell.frame;
cellFrame.size.height += difference;
self.editedCell.frame = cellFrame;
self.contentSize = CGSizeMake(self.contentSize.width, self.contentSize.height + difference);
self.contentOffset = CGPointMake(self.contentOffset.x, self.contentOffset.y + difference);
}
self.editedNote.comments = textView.text;
}
In Textview value change delegate, use this code to resize particular cell without any flickering and all.
But before that, make sure you have used dynamic tableview cell height.
UIView.setAnimationsEnabled(false)
DispatchQueue.main.async {
self.tableView.beginUpdates()
self.tableView.endUpdates()
self.tableView.layer.removeAllAnimations()
}
UIView.setAnimationsEnabled(true)
I have a UITableView displaying cells. I am using Auto Layout. At some point, I add cells to a section of my tableView and I need to animate a subview of each cells as soon as the cells are visible.
I wrote the following method to animate the content of my cell:
- (void)animateWithPercentage:(NSNumber *)percentage
{
self.progressViewWidthConstraint.constant = [percentage intValue];
[self.progressView setNeedsUpdateConstraints];
[UIView animateWithDuration:0.6f animations:^{
[self.progressView layoutIfNeeded];
}];
}
The method works as expected but I don't know when to call it.
If I call it when - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath is called, I end up animating the whole layout of my cell because the cell did not layout itself before.
I also tried calling it in layoutSubviews but the animation is skipped. I guess at this point it is too early to animate anything.
Any idea where my method should be called?
I've gotten this kind of animation to work by calling performSelector:withObject:afterDelay: in either awakeFromNib or didMoveToSuperview in the custom cell's class. It works even with a delay of 0, but it doesn't work if you just use performSelector. I've noticed that with this method, if some of the cells are off screen, they don't update until they are scrolled on, and the scrolling animation ends. You can get the animation to start as soon as the cell scrolls on screen, even while the scrolling animation is still in progress by using dispatch_async instead.
-(void)awakeFromNib {
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:2 animations:^{
self.rightCon.constant = 150;
[self.contentView layoutIfNeeded];
} completion:nil];
});
}
Try to put your method in -(void)didMoveToSuperview
I have a UICollectionView. I want a behaviour where when the user touches a cell, it scales down, as if it is getting pushed down slightly. I've accomplished this by using the UICollectionViewDelegate methods:
- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
[self scaleDownCell:cell];
}
- (void) collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
[self scaleUpCell:cell];
}
My problem is that sometimes my cell will start to scale down, and then suddenly go back to full size, without performing the scale up animation, creating jerky effect. I have checked with breakpoints that it scales up before my scale up method is called. I am trying to figure out why.
The scale functions are as follows:
+ (void)scaleDownCell:(UICollectionViewCell *)cell
{
CGAffineTransform transform = CGAffineTransformMakeScale(0.95, 0.95);
[UIView animateWithDuration:SCALE_DOWN_ANIMATION_DURATION
delay:0.0
usingSpringWithDamping:SCALE_DOWN_SPRING_DAMPING
initialSpringVelocity:SCALE_DOWN_SPRING_VELOCITY
options:0
animations:^{
cell.transform = transform;
}
completion:^(BOOL finished) {
}];
}
+ (void)scaleUpCell:(UBCollectionViewCell *)cell
{
CGAffineTransform transform = CGAffineTransformMakeScale(1.0, 1.0);
[UIView animateWithDuration:SCALE_UP_ANIMATION_DURATION
delay:0.3
usingSpringWithDamping:SCALE_UP_SPRING_DAMPING
initialSpringVelocity:SCALE_UP_SPRING_VELOCITY
options:0
animations:^{
cell.transform = transform;
}
completion:^(BOOL finished) {
}];
}
EDIT: I basically want to replace highlighting in my cells with this scaling behaviour, it serves the exact same purpose and I want it to happen in exactly the same situation that the cell would be highlighted, so it seemed like an appropriate place to apply my transformation.
Regardless, I had first tried overriding the UIResponder touch methods in the cell subclass, but was observing the same behaviour. The sudden scale up happens even if I delete the scale up method so that it is never called, so my scale down is not being cancelled by the scale up animation. I can see through log statements that my scale down is called once, and the animation completion block is getting called with finsished == YES. The cell is getting scaled back up BEFORE the completion block is called (I have set the animation duration to be extra long to help in debugging). This seems to happen when I touch on the cell and then quickly scroll, so I guess the scrolling has something to do with it.
The problem is that you are hijacking the notion of "highlighting" for something that it isn't. Highlighting is a complicated and confusing business. In the course of being tapped and selected, the cell highlights and unhighlights more than once. Thus it is the wrong thing to respond to.
If what you are trying to detect is a touch, then you should respond to touch. If a gesture recognizer won't do, then use a cell subclass of your own so that you can implement UITouch detection directly.
One more thing to keep in mind is that, the way you've written this, if the scale up happens while the scale down is in progress, it will just kill it dead.
EDIT - One final thought - in your edit, you make a comment about scrolling having something to do with this. That makes sense. Think how a collection view layout works - it is responsible for the attributes of each cell, including its transform (https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionViewLayoutAttributes_class/Reference/Reference.html). So naturally if you change the transform of the cell yourself, and you don't tell the layout, and the layout takes charge, it will cancel your transform.
This question already has answers here:
How to stop UITableView from clipping UITableViewCell contents in iOS 7
(2 answers)
Closed 6 years ago.
I've rumbled through new UI info from apple - didn't help.
Now let the code and the screenshots show you the problem i've ran into.
To ensure that is not my buggy code, i've created a new project, with a single file - a UIViewController that has a tableView inside id. the delegates are set.
I do the following:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"UITableViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [NSString stringWithFormat:#"%d",indexPath.row];
// Configure the cell...
UIView * redView = [[UIView alloc] initWithFrame:CGRectMake(0, -10, 100, 20)];
redView.backgroundColor = [UIColor redColor];
[cell addSubview:redView];
return cell;
}
The table view is set on Grouped.
Lets run it on iOS 6:
Duh ofcourse, the Y origin is negative!
Yes, it is, and this is the result I am trying to achieve.
Lets see what it shows on iOS 7:
Hint: that doesn't occur if we add redView to a normal UIView.
Another hint: if i set tableView's background color blue, the gray lines between sections would be blue, not gray (as a demonstration that the gray is not a set header).
Another hint: same goes for ipad.
Why in iOS 7 it cuts everything that goes out of bounds? Please help!
It's because iOS 7 introduced some changes to the view hierarchy of UITableViewCells.
It used to be UITableViewCell view -> contentView.
Now it's more like UITableViewCell view -> scrollView -> contentView.
The solution is to set clipsToBounds = NO on the scrollView (which is set to YES by default). And the way to achieve that is through the superview property.
So basically in iOS6 and prior, to allow content to spill out of the cell bounds, you would do:
self.clipsToBounds = NO; //cell's view
self.contentView.clipsToBounds = NO; //contentView
In iOS7 you have to also prevent the scrollview from not clipping so you'd do something like:
self.clipsToBounds = NO; //cell's view
self.contentView.clipsToBounds = NO; //contentView
self.contentView.superview.clipsToBounds = NO; //scrollView
And the backwards compatible solution I use is:
self.clipsToBounds = NO;
self.contentView.clipsToBounds = NO;
if ([self.contentView.superview isKindOfClass:[NSClassFromString(#"UITableViewCellScrollView") class]]) self.contentView.superview.clipsToBounds = NO;
Keep in mind this is Hacky™ and if the view hierarchy changes again in iOS 8, you might be in trouble. Unfortunately it seems Apple doesn't want us to spill content out of UITableViewCells so AFAIK this is the only workable solution.
The following will fix it:
cell.layer.masksToBounds = NO
However, it will probably break something else, e.g. cell animations.
The problem you are having is caused by the fact that cells just don't support drawing content out of their bounds (actually, using subviews that extend the bounds of their superview is always a hacky solution).
The best advice is to redesign and avoid such funcionality at all.
Another solution could be to add the same view to the bottom of the previous cell or just add them as a subview of UITableView directly.
I don't think that the contents are actually trimmed in a masksToBounds-sense, but instead that the views are covered by the other (opaque) cells. You could try to fix it by changing the order ("z-Index") of the UITableView's subviews, using bringSubviewToFront: and similar methods whenever a cell appears to ensure that the cell closest to the bottom edge of the screen is the frontmost view of all the cells.
I have a UIImageView that is supposed to cover the selected row from a UITableView. Both, the UIImageView, and UITableView are added to the main View inside Interface Builder. Instead of covering up the cell when it is selected, the UIImageView moves to the selected cell and disappears behind it. What I need it to do is move up to the selected row, and cover it up (i.e. the image should be in front of the cell, not behind it). Please also bear in mind that my image needs to only cover the selected row. Here is my relevant code:
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[UIView animateWithDuration:.3 animations:^{
//CGRect rect = [tableView rectForRowAtIndexPath:indexPath];
CGRect rect = [self.view convertRect:[tableView rectForRowAtIndexPath:indexPath] fromView:tableView];
_imageView.frame = rect;
}];
}
Can anyone see what I am doing wrong, and know how to fix this?
Thanks in advance to all who reply.
It is simply because the imageView is added before the table view in the subviews, I guess. Hence you can use bringSubviewToFront method and apply it to the imageView after you add your tableView to the subviews, for example.