Nice ish transitions from a photo thumbnail to a full screen view.

The method of animating the appearance of views in Cocoa is good and simple I think. You can use an inbuilt one, which just works, or you can start an animation block and effectively program in key frames. To make it such that you can click a button containing an image and a view scales out from that image I implemented this:

--SNIP

When the photo button is clicked it will either present the user with a dialogue to choose a photo if the view is in edit mode or instantiate a UIImageView over the button and zoom it out to full screen. Seeing as I opted to display the image in the main window of the app it is above everything else including the navigation bar. For that reason we don’t want anybody to manipulate any other aspect of the system so we turn of user interaction. If we did not do this they could hit the back button and load another recipe. The view we have loaded would remain on top until the user quit the app. At the moment as soon as the animation completes the view is removed. The next stage is prettify it by doing the following:

– Add in a view behind the image view that is dk grey and opaque. This will fill the whole screen after the zoom thus ‘greying’ out the other components.
– Load in a button over the top with user interaction enabled. A click will remove the UIWindow subview and restore user interaction to the recipe view and the nav bar.

I did have this wrapped up in a view controller but it’s a pain to push a view from a controller and maintain all the ‘control’ you want perversely enough. It could just be me but I could not get the frame of a view inside my custom controller to render in the right place. To make the code that presents and animates the view as simple as possible I suspect I will hand code a UIView (without a controller) that can be used anywhere. Now if only I could figure out the location and size of the button that was clicked programatically it would be nice and shiny and generic.

UPDATE: New version of code

- (IBAction)photoButton {
	// If editing display image picker.
	if(self.editing) {
		UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
		imagePicker.allowsEditing = TRUE;
		imagePicker.delegate = self;
		[self presentModalViewController:imagePicker animated:YES];
		[imagePicker release];
	} else {
		// Stop people being able to navigation away or change the UI before we manipulate the view.
		self.navigationController.navigationBar.userInteractionEnabled=NO;
		self.view.userInteractionEnabled=NO;
		// Initialise UIImageView over the top of the existing button.  
		// Be nice to discover this programatically.
		photoView = [[UIImageView alloc] initWithFrame:CGRectMake(8, 72, 72, 72)];
		// Assign photo
		[photoView setImage:recipe.photo];
		// Maintain aspect
		[photoView setContentMode:UIViewContentModeScaleAspectFit];
		// We really! want this window over the top of everything, inc. the nav bar.
		UIWindow *mainWindow = [[UIApplication sharedApplication] keyWindow];
		// Add the subview
		[mainWindow addSubview:photoView];
		// Name our animation block
		[UIView beginAnimations:@"Expand" context:nil];
		// Set the duration
		[UIView setAnimationDuration:0.3f];
		// Tell it where we want to end up, e.g. full frame and zoomed as such
		[photoView setFrame:CGRectMake(0, 0, 320, 480)];
		photoView.transform = CGAffineTransformMakeScale(1, 1);
		// Make sure we configure something to remove the view.
		[UIView setAnimationDelegate:self];
		[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
		[UIView commitAnimations];
	}
}

- (void)didViewPhoto:(id)selector {
	// Remove button immediately or it can be clicked twice, which is bad as it means it will be removed twice and thus crash.
	[photoViewButton removeFromSuperview];
	// Animoot the dissappearance.
	[UIView beginAnimations:@"Shrivel" context:nil];
	[UIView setAnimationDuration:0.3f];
	[photoView setFrame:CGRectMake(8, 72, 72, 72)];
	//[photoView setTransform:CGAffineTransformMakeScale(0.1, 0.1)];
	[UIView setAnimationDelegate:self];
	[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
	[UIView commitAnimations];
}

- (void)animationDidStop:(NSString *)animationID finished:(BOOL)finished context:(void *)context {
	// If it's expand we're zooming in and want to show controls.
	// Otherwise we've viewed the photo and want to get back to normal.
	if (animationID == @"Expand") {
		photoViewButton = [[[UIButton alloc] initWithFrame:CGRectMake(0, 0, 320, 480)] autorelease];
		[photoViewButton addTarget:self action:@selector(didViewPhoto:) 
				  forControlEvents:UIControlEventTouchDown];
		[[[UIApplication sharedApplication] keyWindow] addSubview:photoViewButton];
	} else {
		// Remove from main window
		[photoView removeFromSuperview];
		// Re-enable controls.
		self.navigationController.navigationBar.userInteractionEnabled=YES;
		self.view.userInteractionEnabled=YES;
		
	}
}

Enhancements here would be farming it out to it’s own UIView then you just have to animate that sucker and can make it rotate on the screen. Another possibility…. would be to zoom the actual button itself out and back. If you notice the zoom transition has a small glitch at the end due to the removal of the UIImageView and the UIButton behind it, if the UIButton itself was zoomed in and out it would make the whole code a lot simpler, assuming you can. If I fiddle with it later I’ll post it as a new post and update this one accordingly. For now, it’ll do. Font rendering issues next.

This entry was posted in Programming and tagged , , , . Bookmark the permalink.