I currently have a collection view with a grid of images on it, when selecting an image this segues to another collection view with a full screen image on it.
To do this I am using:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"collectionView"])
{
QuartzDetailViewController *destViewController = (QuartzDetailViewController *)segue.destinationViewController;
NSIndexPath *indexPath = [[self.collectionView indexPathsForSelectedItems] objectAtIndex:0];
destViewController.startingIndexPath = indexPath;
[destViewController.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
[self.collectionView deselectItemAtIndexPath:indexPath animated:NO];
}
}
and then in my detail view:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (_startingIndexPath) {
NSInteger currentIndex = floor((scrollView.contentOffset.x - scrollView.bounds.size.width / 2) / scrollView.bounds.size.width) + 1;
if (currentIndex < [self.quartzImages count]) {
self.title = self.quartzImages[currentIndex][#"name"];
}
}
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
layout.itemSize = self.view.bounds.size;
[self.collectionView scrollToItemAtIndexPath:self.startingIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
}
when I rotate to landscape the image disappears and the title at the top changes, if I go back to the grid and then select a cell again it will segue again to the detail view but the title is for a different cell and the image shows half and half between two different images.
I am getting the following message as well on the log when I rotate the device:
2013-06-04 17:39:53.869 PhotoApp[6866:907] the behavior of the UICollectionViewFlowLayout is not defined because:
2013-06-04 17:39:53.877 PhotoApp[6866:907] the item height must be less that the height of the UICollectionView minus the section insets top and bottom values.
How can I correct this so it will support orientation.
Thanks
You need to invalidate collection view's layout since sizes of cells in portrait orientation are too big for landscape orientation.
Writing something like this in your parent UIViewController should fix your issue:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[self.collectionView.collectionViewLayout invalidateLayout];
}
Related
How to get the frame (CGRect value) of a whole section in a UICollectionView instance?
I know that UITableView has a rectForSection: method. I am looking for a similar thing for UICollectionView.
I checked the documents for UICollectionViewFlowLayout but couldn't find anything that looks like rectForSection:.
to collectionView add category
#implementation UICollectionView (BMRect)
- (CGRect)bm_rectForSection:(NSInteger)section {
NSInteger sectionNum = [self.dataSource collectionView:self numberOfItemsInSection:section];
if (sectionNum <= 0) {
return CGRectZero;
} else {
CGRect firstRect = [self bm_rectForRowAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
CGRect lastRect = [self bm_rectForRowAtIndexPath:[NSIndexPath indexPathForItem:sectionNum-1 inSection:section]];
return CGRectMake(0, CGRectGetMinY(firstRect), CGRectGetWidth(self.frame), CGRectGetMaxY(lastRect) - CGRectGetMidY(firstRect));
}
}
- (CGRect)bm_rectForRowAtIndexPath:(NSIndexPath *)indexPath {
return [self layoutAttributesForItemAtIndexPath:indexPath].frame;
}
#end
Collection view section frame is depend on the collection view layout.It might be flow layout or custom layout. So you will not got the any predefined solution. But you can calculate it for your layout using
UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath];
UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
I will suggest you to subclass UICollectionViewLayout and put you section frame calculation code inside it, so that you section frame will always be the updated one.
NOTE : These point will not give you superset of rect in case of section start point and end point not vertically aligned.To be honest there is no better way to get section frame.
The cell size of UICollectionView is the full screen size. And the user can swipe horizontally. The problem is during the orientation, I can see the next or previous cell. (Attached screenshot here. the red cell is current one, the purple cell is the next cell.) And sometimes after orientation, collection view will scroll to wrong cell index. (I use scrollToItemAtIndexPath to force change it back to correct one...)
I tried two ways to solve this problem.
1) use flow layout:
- (UICollectionViewLayoutAttributes*)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
// after orientation, scroll to the correct position.
UICollectionViewLayoutAttributes* attr = [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath];
[self.collectionView scrollToItemAtIndexPath:itemIndexPath atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
return attr;
}
- (void)prepareForAnimatedBoundsChange:(CGRect)oldBounds
{
NSLog(#"%# prepare animated bounds change", self);
[super prepareForAnimatedBoundsChange:oldBounds];
}
- (void)finalizeAnimatedBoundsChange
{
NSLog(#"%# finalize animated bounds change", self);
[super finalizeAnimatedBoundsChange];
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
CGRect oldBounds = self.collectionView.bounds;
if (!CGSizeEqualToSize(oldBounds.size, newBounds.size)) {
if(oldBounds.size.width > newBounds.size.width)
{
// vertical cell size
self.itemSize = CGSizeMake(768, 968);
}
else{
// horizontal cell size
self.itemSize = CGSizeMake(1024, 712);
}
NSLog(#"%# should invalidate layout", self);
return YES;
}
return NO;
}
2) use in delegate call
// used in will rotate and did rotate, but both does not work well...
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
// do invalidate layout
[self.CollectionView.collectionViewLayout invalidateLayout];
}
// then change the cell size
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
UIInterfaceOrientation o = [[UIApplication sharedApplication] statusBarOrientation];
if (UIInterfaceOrientationIsPortrait(o)) {
return CGSizeMake(768, 968);
} else {
return CGSizeMake(1024, 712);
}
}
I do not want to use fake image to hide layout changes during animation. Thanks!! :)
I have a UITextView in a UITableViewCell. They both dynamically size itself to fit. The problem I have is that when the texview reaches below the bottom of the screen (from making new lines), the tableView does not scroll to its location.
I tried this code, but it didn't work.
- (void)textViewDidChangeSelection:(UITextView *)textView {
[textView scrollRangeToVisible:textView.selectedRange];
}
You can find the cell containing the textView and tell the table view to scroll to that cell:
UIView *parentView = textView.superview;
while(![parentView isKindOfClass:[UITableViewCell class]]) {
parentView = parentView.superview;
}
NSIndexPath *indexPath = [self.tableView indexPathForCell:(UITableViewCell*)parentView];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
You should add code below to scroll the tableView too:
- (void)textViewDidChangeSelection:(UITextView *)textView {
[textView scrollRangeToVisible:textView.selectedRange];
// since you are only scrolling the table a little bit and not by a whole cell, then change the scrollView.offset
CGPoint offset = tableView.contentOffset;
offset.x += 20; // an example. You need to change this based on the new size of the textview.
tableView.contentOffset = offset;
}
i have a collection view, with cells in a size of the all screen.
i'm trying to add spacing between the cells, so when ever i scroll between the cells there will be some space between - as in the iOS photo gallery, when you scroll between full screen images.
obviously i have paging enabled.
but for some reason when i scroll between the cells, the spacing is staying on the screen.
i.e. if the cell width is 320, and the space between the next cell is 20px, so when i scroll to the next cell, the space is reaching to x=0, and the new cell starts at x=20.
why is that?
how do i get the exact iOS photo gallery scrolling full screen images effect?
increase the width of the collection to the space value and shift collection left to the half space value. then change right and left section insets to the half space value
or you can make your own paging that will works even if cell size are less then collection view:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
CGPoint contentOffset = scrollView.contentOffset;
CGSize size = scrollView.frame.size;
CGPoint locationInView = CGPointMake(contentOffset.x + size.width/2, contentOffset.y + size.height/2);
if (scrollView == _collectionView) {
NSIndexPath *indexPath = [_collectionView indexPathForItemAtPoint:locationInView];
[_collectionView selectItemAtIndexPath:indexPath animated:YES scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
[self collectionView:_collectionView didSelectItemAtIndexPath:indexPath];
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate) {
[self scrollViewDidEndDecelerating:scrollView];
}
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
}
I have a split view controller: view & tableview. In this table view I have custom cells with text field. I don't know how many cells will I have, so it generate automatically. And now I'm trying scroll to textfield, when it becomeFirstResponder. I've tried something like this:
-(void) textFieldDidBeginEditing:(UITextField *)textField {
CGPoint focusOnTextField = CGPointMake(0, 300 + textField.frame.origin.y);
[scroller setContentOffset: focusOnTextField animated: YES];
}
300px - start position of my TableView. All seems alright, but textField.frame.origin.y always equal to 0 (like and bounds.origin.y btw).
I thought I can solve a problem if get position of cell, which textfield is active, and then replace textField.frame.origin.y on cell.frame.origin.y or something like this.
===================================================================
I forgot say that my tableviews scroll is disabled. I follow your advices and code examples and solve it like that:
- (UITableViewCell *)cellWithSubview:(UIView *)subview {
while (subview && ![subview isKindOfClass:[UITableViewCell self]])
subview = subview.superview;
return (UITableViewCell *)subview;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
UITableViewCell *activeCell = [self cellWithSubview:textField];
float offsetValueY = 200 + activeCell.origin.y;
CGPoint focusOnTextField = CGPointMake(0, offsetValueY);
[scroller setContentOffset:focusOnTextField animated:YES];
}
And know what? It's working! :-) But it create a new problem. When I begin editing textfield, scroller at first jump on top, and only then going to it correct position. When I write [scroller setContentOffset:focusOnTextField animated:NO]; and this problem disappear, but there is no smooth move of scroller. And this is bad to me :-) So how we can solve it?
Here's how to scroll to a cell containing a text field ...
// find the cell containing a subview. this works independently of how cells
// have been constructed.
- (UITableViewCell *)cellWithSubview:(UIView *)subview {
while (subview && ![subview isKindOfClass:[UITableViewCell self]])
subview = subview.superview;
return (UITableViewCell *)subview;
}
Your idea is correct to trigger that action when editing begins. Just do it using the cell...
- (void)textFieldDidBeginEditing:(UITextField *)textField {
// find the cell containing this text field
UITableViewCell *cell = [self cellWithSubview:textField];
// now scroll using that cell's index path as the target
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
If you add the textfield in the UITableVieCell content view (added by default if you are using .xib) then you have to call something like textField.superview.superview and this will give you the parent cell. If you add the text field directly to the cell view then you have to textField.superview.
[tableView scrollToRowContainingComponent:textField atScrollPosition: UITableViewScrollPositionMiddle animated:YES];
after adding the following category to UITableView:
#implementation UITableView (MyCategory)
-(NSIndexPath*)indexPathOfCellComponent:(UIView*)component {
if([component isDescendantOfView:self] && component != self) {
CGPoint point = [component.superview convertPoint:component.center toView:self];
return [self indexPathForRowAtPoint:point];
}
else {
return nil;
}
}
-(void)scrollToRowContainingComponent:(UIView*)component atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated {
NSIndexPath *indexPath = [self indexPathOfCellComponent:component];
if(indexPath) {
[self scrollToRowAtIndexPath:indexPath atScrollPosition: scrollPosition animated:animated];
}
}
#end