I'm animating the height of a UITableView so it aligns with the keyboard's animation like in this article: //http://macoscope.com/blog/working-with-keyboard-on-ios/
However, I am now getting unwanted sideways animations in my cells as they come in.
This used to work when I was adding the contents to the cell view directly because I would override the UITableViewCell method:
- (void)layoutSubviews
{
[UIView performWithoutAnimation:^{
[super layoutSubviews];
}];
}
But now I'm adding the contents to contentView like you're supposed to. I need to so the ios8 auto resizing works. But the solution of laying out subviews without animation no longer seems to apply to content view.
(I just tried not adding things to contentView and the auto sizing still works actually. I'm still interested in knowing how to properly get contentView contents to not have unwanted animations)
Related
I have a UICollectionView which basically appears like a table, it has a horizontal stack of UICollectionViewCell views. I want the standard UIViewController layout margins (16pt on the left and right on an iPhone X) to apply to the contents of the cells. This happens in a UITableView.
For some reason my cells initialise with the out of the box margins (8pts) and never update.
I have checked all these options in my cell's .xib, and can log out the layoutMargins property to reveal that the value is correct, but the subviews just never move.
Any ideas?
It turns out that, although you can't see it in Interface Builder, a UICollectionViewCell has a content view, which does not by default inherit layout margins.
This solved it for me:
- (void)awakeFromNib
{
[super awakeFromNib];
self.contentView.preservesSuperviewLayoutMargins = YES;
...
}
Be careful on devices/views with safe area insets set, this may knock around the bottom insets of your cell.
I have a UITableView added to a UIViewController inside a UIView. The table view has constraints to fit the size of it's parent view. The view has constraints for it's position and size. If I animate the size of this view, the table view shows new rows, but the new elements, which about to appear, seem to fly around from their initial (not set) position. For example, the right detail indicator of the UITableViewCell flies from the left edge of the cell, the text labels from a slightly different position, etc.
I do the animation like this:
[self.tableView reloadData];
[UIView animateWithDuration:0.3f animations:^{
self.tableViewHeightConstraint.constant = blah;
[self.view layoutIfNeeded];
}];
As you can see, the data reloading takes place before the animation block. Once the animation was played, changing the view size does not animate the table view's contents anymore (I guess it does, but everything is in their place already.)
How can I prevent animating the elements and keep the animation of the frame?
Have a look at calling setAnimationsEnabled on the cells or maybe the cells accessory view.
My pure AutoLayout UITableViewCell looks like this in Interface Builder:
UITableViewCell
|-> UITableViewCell.contentView
|-> UIView (ScrollViewContainerView)
|-> UIScrollView
|-> left (fixed)
|-> center (fill remaining)
|-> right (fixed)
The UIScrollView contains a left, center, and right UIView. left and right are both fixed width, while center expands to fill the remainder of the UIView. The UIScrollView constraints are to align all edges to ScrollViewContainerView. ScrollViewContainerView constraints are to align all edges to the UITableViewCell.contentView. I have a constraint on center's width to be a multiple of ScrollViewContainerView's width, so the UIScrollView scrolls left and right, but the height is fixed and does not scroll. Note that the UIScrollView has been subclassed to include this code so that the UITableView can detect a tap on the cell to toggle selection.
The issue is that I currently can either scroll the UITableView containing these UITableViewCells up and down or I can scroll the UIScrollViews in the UITableViewCells left and right, not both.
When ScrollViewContainerView.userInteractionEnabled == YES, I can't scroll the UITableView up and down, but I can scroll the UIScrollView left and right. When ScrollViewContainerView.userInteractionEnabled == NO, I can scroll the UITableView up and down, but I can't scroll the UIScrollView left and right. userInteractionEnabled == YES on everything else in the above hierarchy.
I can get away with having ScrollViewContainerView as a sibling view to the UIScrollView (making the UIScrollView the direct descent of contentView -- can't get rid of this view completely, because I require it to get the dimensions for the UIScrollView frame). In that case, the opposite handling with userInteractionEnabled holds.
I know I've done this before in other projects before, but starting fresh again, I can't seem to figure out what step I'm missing. Currently using Xcode 6 6A215l targeting iOS 8, though I have reproduced the issue under Xcode 5 targeting iOS 7.
It sounds like the scrollview is causing your tableview to not allow userInteraction when being scrolled. I'm sure that if you called - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView in the UIScrollView delegate (not sure for iOS 8), but you could just do
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
if(scrollView.dragging == YES) {
self.<scrollViewName>.userInteractionEnabled = NO;
}
}
This is untested code, but it's just a bit of help to get you where you need to go.
Hope it helps!
I met some similar problem.
I have a scrollView in tableViewCell. All works fine.
Until one day, someone told me that the tableView can't scroll up/down when finger is touched on the scrollView in 6p. Just in 6p, not in 5, 5s,or6.
This makes me almost crazy.
Finally, I set the scrollView's height smaller than the height in storyboard.
Biu ~ It works~~~
Still, I don't know why.
#user2277872's answer put me on the right track to look at the output of the UIScrollView delegate methods of the UIScrollView in my UITableViewCell subclass. Putting an NSLog() in scrollViewWillBeginDragging: made me notice that the UIScrollView was receiving scrolling events while I was trying to scroll the UITableView. My UIScrollView had a contentSize larger than its frame in both directions, but I've forced that view to only scroll horizontal, so ignored the height and reset it. That force was my undoing and I should have known it at the time -- the correct solution is to fix the frame height. If the UIScrollView doesn't think there is more vertical content, it will correctly forward the swipe up/down gesture to the UITableView.
While I attempt to figure out why my contentSize is too large when it wasn't before (thinking I'm missing a clipToBounds somewhere), what I'm doing to force horizontal scrolling temporarily is (in the UITableViewCell's subclass):
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGSize contentSize = self.scrollView.contentSize;
contentSize.height = self.frame.size.height;
self.scrollView.contentSize = contentSize;
}
EDIT: Actually, this is seemingly better than overriding drawRect. This would be in the UIScrollView subclass:
/*
* Lock to horizontal scrolling only.
*/
- (void)setContentSize:(CGSize)contentSize
{
[super setContentSize:CGSizeMake(contentSize.width, 1)];
}
The height struct member isn't too important, as long as it's guaranteed to be smaller than the frame.size.height of the UITableViewCell. Still hacky, still need to find why I could clip before and not now.
I cannot resize the uitableview properly when changing the orientation of the screen. I can make one orientation work fine but not both.
I've tried 2 different methods: 1) using the autoresizingMask on the tableview and 2) using the layoutSubviews method and here are the results of each:
1) using the autoresizing almost works if I use
self.myTableView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin;
It shows correctly when in portrait and then I move to landscape it also shows correctly but not all the way to the end of the screen. In landscape I see a space on the left side and on the right side.
So, I thought let's just add the UIViewAutoresizingFlexibleWidth to the line of code above. It does make the uitableviewcell expand to the full screen in landscape but when I change the orientation it does not maintain the same cell on the screen and sometimes it displays half of one cell and half of another cell in the middle of the screen.
2) Using auto layout. I get the same behavior as above when I use this code:
-(void)layoutSubviews {
[super layoutSubviews];
onLoadSize=self.contentView.bounds.size;
self.myTableView.frame = CGRectMake(0, 0, onLoadSize.width, onLoadSize.height);
self.selectedBackgroundView.frame = self.myTableView.frame;
}
If I add an if statement testing for the orientation, then it works fine for that orientation in specific but not the other. If I test for both orientations I get the same behavior as described above.
Any help will be very welcome.
Thanks!
UITableViews usually occupy the full space of the parent view controller and typically don't need constraints or active resizing, unless you have more than one table view in a single view.
Consider managing the content of UITableViewCells (instead of managing the table view) by:
(1) Applying constraints to each custom cell in Interface Builder.
(2) Redrawing the cell's frame to fit the parent UItableView when the cell renders its subviews (layoutSubviews method).
(3) Calling [cell.contentView needsUpdateConstraints] to force the cell contents to redraw.
Here is some sample code for cell's custom class:
-(void)layoutSubviews
{
resizeCell:self forView:[the cell's UITableViewController.view]
}
Here is a generic function you can add anywhere for reuse:
-(void)resizeCell:(UITableViewCell *)cell forView:(UIView *)view
{
cell.contentView.frame=CGRectMake(cell.contentView.frame.origin.x,
cell.contentView.frame.origin.y,
view.frame.size.width,
view.frame.size.width,
cell.contentView.frame.size.height);
}
Please note the cell's UITableViewController must be passed to the cell's custom class (as a member variable) for the code above to work.
Good luck!
In my UIViewContoller's subclass, ViewWillAppear asks whether or not there's any data to present, and if there is, changes the UINavigationController's prompt accordingly. This triggers an animation as the prompt pops into view, causing the UINavigationBar to grow in size. When this happens it partially occludes the cells in the top row of the UICollectionView.
I have a vertical Auto Layout constraint of 0, seemingly pinning the UICollectionView to its nearest neighbor, which should be its superview, but the navbar still blocks the top halves of the cells. I've tried everything — telling the CollectionView to layout its subviews, reloading data, etc., but nothing seems to work. Any idea what's going wrong?
- (void)viewWillAppear:(BOOL)animated{
if(self.orderedURLSet.count == 0){
self.navigationItem.prompt = nil;
[self.collectionView setNeedsDisplay];
} else {
self.navigationItem.prompt = #"Tap photos to edit";
}
[self.collectionView reloadData];
[self.collectionView layoutSubviews];
}
Edit: What makes this even stranger is that when I rotate orientation the collectionViewCells aren't occluded, and the full cells remain visible when I rotate back to portrait orientation. Is there some way I can "trick" my app into thinking its layout has changed and it needs to reposition the cells? LayoutSubviews isn't doing the trick.
Edit: After digging a little more into the UIView documentation, it looks like setNeedsLayout and layoutIfNeeded are really the methods I should be using, and not layoutSubviews. I've tried calling both of them, on navigationController:didShowViewController:animated:, viewWillAppear, viewDidAppear, and viewDidLayoutSubviews to no avail.
Have you tried??
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}