简体   繁体   中英

How to zoom a UIScrollView inside of a UICollectionViewCell?

I'm trying to add a UIScrollView inside of a UICollectionViewCell . The idea is that you can use pinch to zoom the UIScrollView (and with it, the image within), but the scrollview doesn't seem to handle any gesture. I'm guessing they are being caught by the UICollectionView .

I've set the delegate of the UIScrollView to be the UICollectionViewCell , but none of the delegate methods are being called.

EDIT: I've made a github repo with the code (simplified as much as I could). Even though it's just a few lines of code, I cannot see what I did wrong.

EDIT2: After the answer was found, I added the fixes to the above-mentioned repo, hope others find it helpful too :)

https://github.com/krummler/gallery-pinchzoom-example

You might want to try manipulating the UIGestureRecognizers in order to do that. In the GalleryViewController :

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView 
                  cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    GalleryImageCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"galleryImageCell" forIndexPath:indexPath];

    ImageContext *imageContext = [self.images objectAtIndex:indexPath.row];

    cell.imageContext = imageContext;
    [self.collectionView addGestureRecognizer:cell.scrollView.pinchGestureRecognizer];
    [self.collectionView addGestureRecognizer:cell.scrollView.panGestureRecognizer];

    return cell;
}

From Apple's documentation on UIView :

Attaching a gesture recognizer to a view defines the scope of the represented gesture, causing it to receive touches hit-tested to that view and all of its subviews. The view retains the gesture recognizer.

So you'll also want to make sure to remove them when the cell is not showing anymore.

- (void)collectionView:(UICollectionView *)collectionView 
  didEndDisplayingCell:(UICollectionViewCell *)cell 
    forItemAtIndexPath:(NSIndexPath *)indexPath {

    // Get the cell instance and ...
    [self.collectionView removeGestureRecognizer:cell.scrollView.pinchGestureRecognizer];
    [self.collectionView removeGestureRecognizer:cell.scrollView.panGestureRecognizer];
}

Since you're not modifying the UIGestureRecognizer's delegate, only its scope, it will still control the zooming of just that cell's scrollview.

EDIT :

I added the panGestureRecognizer to the above examples, following a suggestion from the OP that it was needed. The zooming itself is completely handled by the pinchGestureRecognizer , but it's true that in most cases, after zooming an image to a point where only a subset of it is visible, you'll want to pan to move the visible portion around. That is, it's part of a proper zooming experience.

I just made an implementation for SWIFT 3 on iOS 9.3+ and all i done was:

1. Place the image view inside a scrollview

故事板的例子

2. Connect the scrollview delegate to collectionview cell class

3. Implement the code below on the collectionview subclass

class FullScreenImageTextDetailCollectionViewCell: UICollectionViewCell, UIScrollViewDelegate {

     @IBOutlet var scrollview: UIScrollView!
     @IBOutlet weak var backgroundImageView: UIImageView!

     override func awakeFromNib() {
         self.scrollview.minimumZoomScale = 0.5
         self.scrollview.maximumZoomScale = 3.5
         self.scrollview.delegate = self
     }

     func viewForZooming(in scrollView: UIScrollView) -> UIView? {
         return self.backgroundImageView
     }
}

No gesture recognizer adding or removing on the parent collectionview controller was necessary, worked like a charm!

Thanks for previous examples for reaching this!

Please add scrollview in your cell and your current cell image view add in scrollview. then use below code:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    ImageContext *context = [self.images objectAtIndex:indexPath.row];
    ImageCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"imageCell" forIndexPath:indexPath];
    cell.cellScrollView.autoresizesSubviews = YES;
    cell.cellScrollView.multipleTouchEnabled =YES;
    cell.cellScrollView.maximumZoomScale = 4.0;
    cell.cellScrollView.minimumZoomScale = 1.0;
    cell.cellScrollView.clipsToBounds = YES;
    cell.cellScrollView.delegate = self;
    cell.cellScrollView.zoomScale = 1.0;

    [cell.imageView setImage:[UIImage imageNamed:context.thumbImageUrl]];

return cell;
}
 -(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
 {   NSLog(@"%i",scrollView.subviews.count);
  for (UIView *v in scrollView.subviews) {
    if ([v isKindOfClass:[UIImageView class]]) {
        return v;
      }
  }

}

Check if all related view's multitouch is on. iOS disabled multitouch by default on most views to save energy.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{            
  if (touch.view == [self myScrollView]) //<- whatever your scrollView is called 
    {
      return YES;
    }
  return NO;
}

Don't know your code but try playing around with the above code to see if you can filter your touches to the objects you want. The above code is from the UIGestureRecognizerDelegate Protocol Reference .

The delegates are not getting called because you added it inside the UICollectionview.The touch events are availabel for the collection view which is the superview and hence not obtainable by the view inside.You may have to think of some other way to achieve this model.

UICollectionView is having the same pattern as UITableView ,The problem occours in tableview inside scrollview ,that the touch event is accepted by scrollview[ which is the superview ] and to make the tableview scrollable in that case ,The scroll of scrollview must be disabled.This is another version of that problem

The apple docs says that the above said case make unpredictable results.So This may be same for your problem.

In my opinion you must go for better design which can achieve what you look for

I've set the delegate of the UIScrollView to be the UICollectionViewCell, but none of the delegate methods are being called.

For that just define one function in collectionViewCell

  @IBOutlet weak var mainScrollView:UIScrollView!
  func setup(){
        mainScrollView.minimumZoomScale=1
        mainScrollView.maximumZoomScale=10.0

        mainScrollView.delegate=self

    }

   func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return yourimageviewinside of scrollview
    }

call this fuction in collectionview(_,cellForItem ) method of collectionview

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM