Is this possible?
I want to change the alpha value of the navigation bar in my view controller (in an animation), but if I do self.navigationController.navigationBar.alpha = 0.0;, the portion of the screen the navigationBar took up totally disappears and leaves a black box, which is not what I'd like (I'd prefer it to be the color of self.view's background).
As I support Colin's answer, I want to give you an additional hint to customize the appearance of an UINavigationBar including the alpha.
The trick is to use UIAppearance for your NavigationBar. This enables you to assign an UIImage to your NavigationBar's backgroundImage. You can generate these UIImages programmatically and use for that UIColors and set the colors' alpha properties as you want. I've done this in one of my own applications and it works as expected.
Here I give you some code snippets:
E.g. in your ..AppDelegate.m add these lines in didFinishLaunchingWithOptions:
//create background images for the navigation bar
UIImage *gradientImage44 = nil; //replace "nil" with your method to programmatically create a UIImage object with transparent colors for portrait orientation
UIImage *gradientImage32 = nil; //replace "nil" with your method to programmatically create a UIImage object with transparent colors for landscape orientation
//customize the appearance of UINavigationBar
[[UINavigationBar appearance] setBackgroundImage:gradientImage44 forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundImage:gradientImage32 forBarMetrics:UIBarMetricsLandscapePhone];
[[UINavigationBar appearance] setBarStyle:UIBarStyleDefault];
Implement convenience methods to programmatically creates UIImage objects, e.g. create a new category for UIImage:
//UIImage+initWithColor.h
//
#import <UIKit/UIKit.h>
#interface UIImage (initWithColor)
//programmatically create an UIImage with 1 pixel of a given color
+ (UIImage *)imageWithColor:(UIColor *)color;
//implement additional methods here to create images with gradients etc.
//[..]
#end
//UIImage+initWithColor.m
//
#import "UIImage+initWithColor.h"
#import <QuartzCore/QuartzCore.h>
#implementation UIImage (initWithColor)
+ (UIImage *)imageWithColor:(UIColor *)color
{
CGRect rect = CGRectMake(0, 0, 1, 1);
// create a 1 by 1 pixel context
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
[color setFill];
UIRectFill(rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
Re-work your image creation in 1. (#import "UIImage+initWithColor.h" in AppDelegate.m and replace the "nil"s):
This is your spot of interest: by changing your colors' alpha property you are influencing the opacity level of you NavigationBar as well!
UIImage *gradientImage44 = [UIImage imageWithColor:[UIColor colorWithRed:1.0 green:0.0 blue:1.0 alpha:0.2]];
UIImage *gradientImage32 = [UIImage imageWithColor:[UIColor colorWithRed:1.0 green:0.0 blue:1.0 alpha:0.2]];
I created a small demo project and add you two screenshots:
the view itself has a yellow backgroundColor.
The backgroundImages of the NavigationBar have a red color.
Screenshot 1 shows a NavigationBar with a value for alpha = 0.2.
Screenshot 2 shows a NavigationBar with a value for alpha = 0.8.
The most straightforward way of doing this is modifying the alpha component of navigationBar background view, which at this time (iOS9) is a first navigationBar subview. Note however that we never know if the subview hierarchy will be changed by apple in later releases, so gotta be careful.
let navigationBackgroundView = self.navigationController?.navigationBar.subviews.first
navigationBackgroundView?.alpha = 0.7
Directly from the Apple Developer reference:
"there are only a handful of direct customizations you can make to the
navigation bar. Specifically, it is alright to modify the barStyle,
tintColor, and translucent properties, but you must never directly
change UIView-level properties such as the frame, bounds,
alpha, or hidden properties directly."
You can however set the translucence property of the navigation bar. If you do [self.navigationController.navigationBar setTranslucent:YES];
should solve your problem. You can also try seeing if any of the UIBarStyle enums are something you want.
MickBraun's answer in Swift:
In AppDelegate.swift add these lines in didFinishLaunchingWithOptions:
// create background images for the navigation bar
let gradientImage44 = UIImage.imageWithColor(UIColor(red: 1.0, green: 0.0, blue: 1.0, alpha: 0.2))
let gradientImage32 = UIImage.imageWithColor(UIColor(red: 1.0, green: 0.0, blue: 1.0, alpha: 0.2))
// customize the appearance of UINavigationBar
UINavigationBar.appearance().setBackgroundImage(gradientImage44, forBarMetrics: .Default)
UINavigationBar.appearance().setBackgroundImage(gradientImage32, forBarMetrics: .Compact)
UINavigationBar.appearance().barStyle = .Default
Implement convenience methods to programmatically create UIImage objects.
class func imageWithColor(colour: UIColor) -> UIImage {
let rect = CGRectMake(0, 0, 1, 1)
// Create a 1 by 1 pixel content
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
colour.setFill()
UIRectFill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
If you just want to get rid of the navigationBar in an animated way you could do:
[self.navigationController setNavigationBarHidden:YES animated:YES];
If you want to control the animation and its necessary to set the alpha to 0.0, read on:
The "black box" you are seeing is from the underlying view or window. If you just want your views color instead of the "black box" do:
self.navigationController.view.backgroundColor = self.view.backgroundColor;
[UIView animateWithDuration:1.0 delay:1.0 options:0 animations:^{
self.navigationController.navigationBar.alpha = 0.0;
} completion:NULL];
If you want your actual view to be where the navigationBar was, you need to increase the height of your view:
[UIView animateWithDuration:1.0 delay:1.0 options:0 animations:^{
self.navigationController.navigationBar.alpha = 0.0;
CGFloat navigationBarHeight = self.navigationController.navigationBar.frame.size.height;
CGRect frame = self.view.frame;
frame.origin.y -= navigationBarHeight;
frame.size.height += navigationBarHeight;
self.view.frame = frame;
} completion:NULL];
You have a few options depending, in part, upon the barStyle of your UINavigationBar. The main thing is realizing that you likely don't necessarily have to animate the alpha property to get the effect you're describing.
UIBarStyleDefault or UIBarStyleBlackOpaque Option A is to set your UINavigationBar translucent property to YES, then animate the alpha:
navigationBar.translucent = YES; // ideally set this early, in the nib/storyboard, or viewDidLoad
...
[UIView animateWithDuration: 1.0
animations: ^{
// toggle:
navigationBar.alpha = navigationBar.alpha == 0 ? 1.0 : 0.0;
}];
In this scenario your view will be positioned behind your navbar, even when its alpha is 1.0. The downside to this scenario is that even with a 1.0 alpha you might see a tinge of your view's background color behind the UINavigationBar. Also, all of your subviews will need to be positioned 44 points down from the top.
UIBarStyleDefault or UIBarStyleBlackOpaque Option B is to hide the navbar in a cross-disolve transition animation. This will expose the superview of the UINavigationBar. If you're using a UINavigationController then the black background of the UINavigationController view is what you'll see - but you can set the background color of the UINavigationController view to match your view to get the effect that you want:
UINavigationBar* navigationBar = self.navigationController.navigationBar;
self.navigationController.view.backgroundColor = self.view.backgroundColor;
[UIView transitionWithView: navigationBar
duration: 1.0
options: UIViewAnimationOptionTransitionCrossDissolve
animations: ^{
// toggle:
navigationBar.hidden = !navigationBar.hidden;
}
completion: nil];
One thing to watch out for with this solution might be a layout issue if the UINavigationController updates your view frame because you hid the UINavigationBar. This would be fine except that your subviews might shift up 44 pixels if they're anchored to the top left. To work around this you might consider anchoring your subviews to the bottom of your view instead (either with springs or with layout constraints).
UIBarStyleDefault or UIBarStyleBlackOpaque Option C is to cover up the UINavigationBar with another view, again using a cross-disolve transition animation:
UINavigationBar* navigationBar = self.navigationController.navigationBar;
[UIView transitionWithView: navigationBar
duration: 1.0
options: UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionAllowAnimatedContent
animations: ^{
// toggle:
const int tag = 1111;
UIView* navOverlayView = [navigationBar viewWithTag: tag];
if ( navOverlayView == nil )
{
navOverlayView = [[UIView alloc] initWithFrame: CGRectInset( navigationBar.bounds, 0, -3 ) ];
navOverlayView.backgroundColor = self.view.backgroundColor;
navOverlayView.tag = tag;
[navigationBar addSubview: navOverlayView];
}
else
{
[navOverlayView removeFromSuperview];
}
}
completion: nil];
UIBarStyleBlackTranslucent: This option is the easiest, as the UINavigationBar is already translucent, and your view is already behind it. Simply animate the alpha:
[UIView animateWithDuration: 1.0
animations: ^{
// toggle:
navigationBar.alpha = navigationBar.alpha == 0 ? 1.0 : 0.0;
}];
You can also try to set a background view underneath the navigation bar and modify this view directly.
private lazy var backgroundView: UIView = UIView()
...
private func setupNavigationBarBackground() {
guard let navigationBar = navigationController?.navigationBar else { return }
navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationBar.isTranslucent = true
view.addSubview(backgroundView)
backgroundView.alpha = 0
backgroundView.backgroundColor = .red
backgroundView.translatesAutoresizingMaskIntoConstraints = false
backgroundView.topAnchor.constraint(equalTo: topLayoutGuide.topAnchor).isActive = true
backgroundView.bottomAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
backgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
backgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
}
private func changeNavigationBarAlpha(to alpha: CGFloat) {
backgroundView.alpha = alpha
}
This should get you the effect you desire:
self.navigationController.navigationBar.alpha = 0.0;
self.navigationController.navigationBar.frame = CGRectMake(0,0,320,0);
Did not try this in an animation, works in my viewDidAppear though, hope it works.
Swift3
var navAlpha = // Your appropriate calculation
self.navigationController!.navigationBar.backgroundColor = UIColor.red.withAlphaComponent(navAlpha)
Related
Here is my code for initializing UIActivityAndicatorView:
self.indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhiteLarge];
self.indicator.color = [UIColor colorWithRed:252.0 green:113.0 blue:9.0 alpha:1.0];
self.indicator.center = self.view.center;
[self.view addSubview:self.indicator];
After animation started - indicator appears in proper place, but with white color. Why color property is ignored?
The values are floats between 0 and 1. Try this:
self.indicator.color = [UIColor colorWithRed:252.0/255.0 green:113.0/255.0 blue:9.0/255.0 alpha:1.0];
It's important to notice that the activityIndicatorViewStyle will override the color property as per the documentation:
If you set a color for an activity indicator, it overrides the color provided by the activityIndicatorViewStyle property.
So you just have to set the style first, then the color:
let activityIndicator = UIActivityIndicatorView()
activityIndicator.activityIndicatorViewStyle = .whiteLarge
let myColor = UIColor.colorWithRed(252.0/255.0, green: 113.0/255.0, blue: 9.0/255.0, alpha: 1.0)
activityIndicator.color = myColor
activityIndicator.startAnimating()
And that should be it.
I'm amazed that the selected answer worked in Dec of 2015, since I cannot get the color to show anything but white no matter what I do in iOS 9.2. This for the Large White spinner.
What I did find that works is to start the control with an alpha of 0, then after a 100 milliseconds change the color and show it. The code below is from my ATMHud fork on github:
- (void)setActivity:(BOOL)activity
{
hudView.showActivity = activity;
if (activity) {
[hudView.activity startAnimating];
hudView.activity.alpha = 0;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^
{
// Workaround for bug where Apple ignores the color property
[self changeColor];
} );
} else {
[hudView.activity stopAnimating];
}
}
- (void)changeColor
{
hudView.activity.color = [UIColor blackColor];
[UIView animateWithDuration:.250+_animateDuration animations: ^{ hudView.activity.alpha = 1; }];
}
Do you have any suggestion to implement the following button animation in an iOS app?
Ok lets split this animation into a few segments to figure it out.
1) We need a white and green circle.
2) We need a tick image.
3) We need to animate the white circle and the tick.
Step One
Add a green UIView to your interface file. This will be the background view. We do not need to animate this view. Then link the green view to a IBOutlet like so:
IBOutlet UIView *greenView;
Step Two
Add a white UIView inside of the green UIView and make it the exact same size as the green UIView. Then link the white UIView to an IBOutlet in you header file like so:
IBOutlet UIView *whiteView;
Step Three
Make or download a 'tick' image and add it to your Xcode project. Then add the tick image ABOVE the green/white views that we just created. Please place the tick above the views and NOT in the views. Then link the tick image to an IBOutlet - this will help us detect when the view has been pressed. Do it like so:
IBOutlet UIImageView *tickImage;
Step Four
We need to change the green and white UIView to circles. We can do this with code. Import the #import <QuartzCore/QuartzCore.h> framework into you header file.
Then in your viewDidLoad method, add the following code which will change the green/white views into circles:
CGPoint greenCenter = greenView.center;
CGPoint whiteCenter = whiteView.center;
CGRect newFrameGreen = CGRectMake(greenView.frame.origin.x, greenView.frame.origin.y, 20, 20);
CGRect newFrameWhite = CGRectMake(whiteView.frame.origin.x, greenView.frame.origin.y, 20, 20);
greenView.frame = newFrameGreen;
greenView.layer.cornerRadius = newSize / 2.0;
greenView.center = greenCenter;
whiteView.frame = newFrameWhite;
whiteView.layer.cornerRadius = newSize / 2.0;
whiteView.center = whiteCenter;
Step Five
In order to run the animation, we need to connect the image view to a method which will run the animation. You can do it by using a UITapGestureRecognizer. In your viewDidLoad method, add the following:
UITapGestureRecognizer *newTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tickAnimation)];
[tickImage setUserInteractionEnabled:YES];
[tickImage addGestureRecognizer:newTap];
Then add the animation method to you implementation (.m) file as well:
-(void)tickAnimation {
}
Step Six
Create a BOOL value in your header file - we will use this to detect if the button is in the "ON" or "OFF" states.
BOOL animationMode;
Then in your viewDidLoad method, set the animationMode bool value to "OFF" - this is because the button will be "OFF" or in the white state by default.
animationMode = NO;
Now we can go on to the animation. From what I can see, this is split up into two animations. The first one is making the white view smaller and the second one is doing a subtle popup animation on the tick image. Update the tickAnimation method in order to perform the animation:
-(void)tickAnimation {
if (animationMode == NO) {
// Button is OFF or set to white, lets set it to ON.
[UIView animateWithDuration:1.0f animations:^{
whiteView.transform = CGAffineTransformMakeScale(0,0);
} completion:nil];
[UIView animateWithDuration:1.0f animations:^{
tickImage.transform = CGAffineTransformMakeScale(2,2);
} completion:nil];
[UIView animateWithDuration:1.0f animations:^{
tickImage.transform = CGAffineTransformMakeScale(0,0);
} completion:nil];
animationMode = YES;
}
else if (animationMode == YES) {
// Button is ON or set to green, lets set it to OFF.
[UIView animateWithDuration:1.0f animations:^{
whiteView.transform = CGAffineTransformMakeScale(1,1);
} completion:nil];
animationMode = NO;
}
}
This is my attempt at making this kind of animation. I hope this helps :)
You could animate the layer.borderWidth property for the colour fill.. It draws inside of the layer, something like this.
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(30, 30, 100, 100)];
[self.view addSubview:button];
button.layer.borderWidth = 10;
button.layer.borderColor = [UIColor greenColor].CGColor;
button.layer.cornerRadius = 50;
CABasicAnimation *border = [CABasicAnimation animationWithKeyPath:#"borderWidth"];
[border setFromValue:[NSNumber numberWithInt:10]];
[border setToValue:[NSNumber numberWithInt:50]];
border.duration = 1.0;
button.layer.borderWidth = 50;
[button.layer addAnimation:border forKey:#"border"];
Here is a really slow gif screen cap of this working with autoReverse set to true.
If you like Swift it looks like this
var button = UIButton(frame: CGRect(x: 30, y: 30, width: 100, height: 100))
self.view.addSubview(button)
button.layer.borderWidth = 10
button.layer.borderColor = UIColor.greenColor().CGColor
button.layer.cornerRadius = 50
let border:CABasicAnimation = CABasicAnimation(keyPath: "borderWidth")
border.fromValue = 10
border.toValue = 50
border.duration = 1.0
button.layer.borderWidth = 50
button.layer.addAnimation(border, forKey: "border")
First, you have an imageView with image like this.
Second, you overlay it with a white UIView, and at the same time, init a UIImageview for the v symbol to turn it into white color.
Next step, run animation to scale the white view from 1 to 0 in 0.2 sec.
When it done, you run other animation to make the white v symbol scale from 1 to 1.2 in 0.2 sec.
That is my idea.
I have a background gradient that draws nicely when the screen is initially rendered in portrait mode. However, when the device is rotated to landscape mode, the right 40% of the screen is black (i.e. the background is not drawn on the part of the screen that wasn't previously drawn). I don't know how to get the background to redraw to fill the entire screen.
Can anyone tell me what I am doing wrong? Thanks!
Here is how it looks when initially drawn.
And here is now it looks when I rotate it:
Here is the code for my background gradient class:
#import "BackgroundGradient.h"
#interface BackgroundGradient()
#property (strong, nonatomic) UIView *view;
#end
#implementation BackgroundGradient
- (id) initWithView:(UIView *) view
{
if (!_view)
_view = view;
return self;
}
- (void) makeGradient
{
UIColor *darkOp = [UIColor colorWithRed:(193.0 / 255.0) green:(215.0 / 255.0) blue:(46.0 / 255.0) alpha: 1];
UIColor *lightOp = [UIColor colorWithRed:(222.0 / 255.0) green:(233.0 / 255.0) blue:(143.0 / 255.0) alpha: 1];
// Create the gradient
CAGradientLayer *gradient = [CAGradientLayer layer];
// Set colors
gradient.colors = [NSArray arrayWithObjects:
(id)darkOp.CGColor,
(id)lightOp.CGColor,
(id)darkOp.CGColor,
nil];
// Update the start and end points
gradient.startPoint = CGPointMake(0.5, 0.0);
gradient.endPoint = CGPointMake(0.5, 1.0);
// Set bounds
gradient.frame = self.view.bounds;
// Add the gradient to the view
[self.view.layer insertSublayer:gradient atIndex:0];
gradient.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:0.7],
[NSNumber numberWithFloat:0.9],
nil];
}
#end
And finally, here is how I instantiate the background object:
- (void)viewDidLoad
{
[super viewDidLoad];
self.background = [[BackgroundGradient alloc] initWithView:self.view];
[self.background makeGradient];
}
EDIT: I've added the following code and included updates to the start and end points (above):
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[self.background makeGradient];
}
I'm almost there, but now, I get this. Notice how there is still a vertical line. It looks to me like the new background is getting drawn behing the previous background such that the previous background is still there and is blocking the new background. This is particularly noticeable as the animation from portrait to landscape is occurring.
This is where you are doing it wrong: CAGradientLayer *gradient = [CAGradientLayer layer]; and [self.view.layer insertSublayer:gradient atIndex:0];
You are 'adding'a new instance of gradient layer each time the device rotates. You shouldn't do that. Instead, only set it's frame on rotation, and everything will work absolutely fine. Use only one instance. Create it in viewDidLoad
Use view debugging to help out in such cases.
I solved this issue with this:
Resize the sublayer of my background image.
Swift
override func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
self.backgroundImageView.layer.sublayers?.first?.frame = self.view.bounds
}
Update gradient's frame in viewDidLayoutSubviews:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// find gradient layer
let gradLayers = gradientView.layer.sublayers?.flatMap { $0 as? CAGradientLayer }
// this assumes there is only one gradient layer
gradLayers?.first?.frame = gradientView.bounds
}
My main UIViewController has a sub UIView named viewContainer. It has a couple of UIImageViews, UIButtons and UIViews added to it. In my viewDidLoad method I've applied the following to viewContainer:
self.viewContainer.layer.shadowColor = [UIColor blackColor].CGColor;
self.viewContainer.layer.shadowOffset = CGSizeMake(0, 0);
self.viewContainer.layer.shadowOpacity = 1.0;
self.viewContainer.layer.shadowRadius = 10.0;
self.viewContainer.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.viewContainer.layer.bounds].CGPath;
self.viewContainer.backgroundColor = [UIColor blackColor];
self.viewContainer.opaque = YES;
When a user presses a button inside the viewContainer, I perform the following translation:
[UIView animateWithDuration:0.3
animations:^{
[self.viewContainer setCenter: CGPointApplyAffineTransform(self.viewContainer.center, CGAffineTransformConcat(self.viewContainer.transform, CGAffineTransformMakeTranslation(60, 0)))];
}
completion:nil];
My problem is that at the end of the animation, the edge of the view flickers for a fraction of a second (a white vertical line). I've tried setting the frame instead of a transform but still the same result. I'm attempting to do a sort of slide to reveal options thing, which works but flickers on completion.
Any ideas?
I'm trying to do the following thing -
I have two version of the same image, one colored and one black and white. I want the B&W to "Slide up" revealing the one under it.
I tried setting the height and the frame, but couldn't get it to work like I want it to.
For example , this would be a combination of both UIImages where one is revealing the other, while not moving:
Any ideas? :)
OK, here is a different method using a mask on the grey view. I actually tested this with a blueView and a greyView, where the grey covers up the blue. Put a mask layer on the grey layer so that the grey layer is visible. Now animate the mask away from the grey layer, making the grey layer disappear without moving it, the blue shows thru where the grey disappears.
If you want to experiment with this, create an empty project in XCode with a single view, and put this code inside viewDidLoad. That's how I tested this.
self.view.backgroundColor = [UIColor whiteColor];
UIView* blueView = [[[UIView alloc] init] autorelease];
blueView.backgroundColor = [UIColor blueColor];
blueView.frame = CGRectMake(50,50,100,100);
UIView* grayView = [[[UIView alloc] init] autorelease];
grayView.backgroundColor = [UIColor grayColor];
grayView.frame = CGRectMake(50,50,100,100);
[self.view addSubview:blueView];
[self.view addSubview:grayView]; // gray covers the blue
// Create a mask to allow the grey view to be visible and cover up the blue view
CALayer* mask = [CALayer layer];
mask.contentsScale = grayView.layer.contentsScale; // handle retina scaling
mask.frame = grayView.layer.bounds;
mask.backgroundColor = [UIColor blackColor].CGColor;
grayView.layer.mask = mask;
// Animate the position of the mask off the grey view, as the grey becomes unmasked,
// that part of the grey "dissappears" and is no longer covering the blue underneath
CABasicAnimation* a = [CABasicAnimation animationWithKeyPath:#"position"];
a.duration = 4;
a.fromValue = [NSValue valueWithCGPoint:mask.position];
CGPoint newPosition = mask.position;
newPosition.y += mask.bounds.size.height;
a.toValue = [NSValue valueWithCGPoint:newPosition];
a.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[mask addAnimation:a forKey:#"colorize"]; // animate presentation
mask.position = newPosition; // update actual position
Don't forget this line at the top with the #imports:
#import <QuartzCore/QuartzCore.h>
One way is to stack the UIView's so that the B&W image on top of the color image, where the color image is completely covered (hidden) by the B&W image.
[self.view insertSubview:bwImage aboveSubview:colorImage];
Only the B&W view is visible. Now set clipsToBounds on the B&W view:
bwImage.clipsToBounds = YES;
Finally animate the height in the bounds of the bwImage so that its height shrinks down to 0, clipping the B&W image and revealing the color image behind it. Something like this:
[UIView animateWithDuration:1.0 animations:^{
CGRect b = bwImage.bounds;
b.size.height = 0;
bwImage.bounds = b;
}];
I haven't tried this but hopefully you get the idea.