Gold Challenge


#1

Ok here is my solution for the zooming gold challenge. Suggestions welcomed. I have done it without subclassing a UIScrollView, which seems a bit unnecessary, but maybe it could be improved by doing that?

Nothing changes in the itemsViewController. In BNRImageViewController.m I declare a property for the imageView in the class extension and add the protocol declaration for UIScrollViewDelegate,

@interface BNRImageViewController ()<UIScrollViewDelegate>

@property (nonatomic, strong)UIImageView *imView;

@end

set up the scrollView in loadView

-(void)loadView
{
//Get a scrollView
    UIScrollView *scrollView=[[UIScrollView alloc]init];
//Get an imageView with the passed in image
    UIImageView *imageView = [[UIImageView alloc] initWithImage:self.image];
//Assign it to the declared imageView variable
    self.imView=imageView;
//Set the initial content size, delegate and zoom limits
    scrollView.contentSize=imageView.bounds.size;
    scrollView.delegate=self;
    scrollView.minimumZoomScale=0.5;
    scrollView.maximumZoomScale=8.0;
//Add imageview to the scrollview
    [scrollView addSubview:self.imView];
//Set the scrollview as the controller's view
    self.view=scrollView;
}

I set the controller’s view here instead of in ViewWillAppear as it was, to save having to create a scrollView property, but maybe this has consequences in some situations…?

Then I set the contentOffset to center the image for the scrollview in viewDidAppear – as I understand it the view has to be visible when this is done, is that correct? I couldn’t set the offset in loadView anyway…

-(void)viewDidAppear:(BOOL)animated
{
    UIScrollView *scrollview=(UIScrollView *)self.view;
    
    scrollview.contentOffset=CGPointMake(self.imView.bounds.size.width/2-scrollview.bounds.size.width/2,self.imView.bounds.size.height/2-scrollview.bounds.size.height/2);
}

and then the delegate methods to enable zooming of the imageView, and conditionally reset the offset to the center when the smallest dimension of the image is smaller than the bounds of the scrollview – this is so it isn’t centred if you are zooming into a corner detail,say.

-(UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return self.imView;
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    UIView *view=self.imView;
    CGFloat ratio=MIN(view.bounds.size.width,view.bounds.size.height)*scrollView.zoomScale;
    
    if (ratio<scrollView.bounds.size.height) {
        CGPoint offset;
        
        offset.x=(view.bounds.size.width*scrollView.zoomScale-scrollView.bounds.size.width)/2;
        offset.y=(view.bounds.size.height*scrollView.zoomScale-scrollView.bounds.size.height)/2;
        
        scrollView.contentOffset=offset;
    }
    
    
}

Seems to work so far. Tried to use setContentOffset:animated: to smooth the zooming transitions, but this seemed to cause a massive performance issue as scrollViewDidScroll must have been called during the animation…as it is it feels a bit jumpy, so any suggestions welcome.


#2

Thanks for posting this pmac, it was a big help. I just couldn’t get the scrollView to work when adding the image through viewWillAppear so ended up cheating and looking at your solution. A few hints from the authors would have been useful for this challenge.
Might I suggest a small improvement though:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    
    UIView *view = self.imageView;
    
    CGPoint offset = scrollView.contentOffset;
    
    if (view.bounds.size.width * scrollView.zoomScale < scrollView.bounds.size.width) {
        offset.x = (view.bounds.size.width * scrollView.zoomScale - scrollView.bounds.size.width)/2;
    }
    if (view.bounds.size.height * scrollView.zoomScale < scrollView.bounds.size.height) {
        offset.y = (view.bounds.size.height * scrollView.zoomScale - scrollView.bounds.size.height)/2;
    }
    
    scrollView.contentOffset = offset;
}

(My self.imageView is the same as your self.imView). This way, when the view becomes smaller than the scrollView in one dimension, it will centre only in that dimension and you can still scroll in the other dimension.


#3

I’m just now starting this challenge, but my idea was just to put a UIPinchGestureRecognizer on the Popover view. My confusion is how to do everything necessary while using the crazy actionBlock.