I'm trying to implement a custom blur view where the blur radius can be animated, and the blur view can stack blending modes (CGBlendingMode or a CIFilter), all while preserving any animations/actions occurring in the background.
I tried UIVisualEffectView first, but the radius cannot be animated without accessing private APIs which would most likely lead to an app rejection.
The problem with taking a snapshot and applying the effects is that the view is static, and any movement in the background is covered up by the blur view.
I also took a look at FlexMonkey's Blurable, but I had similar results as the snapshot.
Any guidance would be really helpful.
Cheers
Edit: I added a gif to demonstrate what I'm trying to make. The red view moving left to right blurs and multiplies the content below it, while showing the animation occurring on the red/blue square behind it.
Blend modes are extremely tricky and I'm not sure it's possible to apply them to a live UIView (GPUImage might help here) but animating the blur radius (between 0 and UIVisualEffectView's full blur) is possible via this method:
You can animate from a nil effect to an effect like UIBlurEffectStyleDark so if we add an animation and then pause the layer's animations we can control the progress of the effect by adjusting the layer's timeOffset!
- (void) setupBlur {
// Setup the blur to start with no effect
self.blurredEffectView = [[UIVisualEffectView alloc] initWithEffect:nil];
// Add animation to desired blur effect
[UIView animateWithDuration:1.0 animations:^{
[self.blurredEffectView setEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
}];
// Pause layer animations
self.blurredEffectView.layer.speed = 0;
}
Adjust blur between 0.0 and 1.0 (animation duration):
- (void) adjustBlur:(CGFloat)blurIntensity {
self.blurredEffectView.layer.timeOffset = blurIntensity;
}
Note: this won't work on iOS 8 where UIBlurEffect animations aren't supported.
Related
Currently, I'm using a UIVisualEffectView to apply a blur to an image.
I have a UIScrollView. As I pull down on the scrollView, in my "scrollViewDidScroll" method, I'm changing the alpha of the UIVisualEffectView. The current behavior of this is that the blur radius changes smoothly as I drag around in the view.
The problem is, of course, I'm not supposed to be doing this. Getting warnings about changing the alpha value of a UIVisualEffectView.
I've seen people say to do a smooth transition of blurring using an animation, like this here: How to fade a UIVisualEffectView and/or UIBlurEffect in and out?
However, I haven't seen anything that allows me to do this during, say a pan-gesture or something. I mean, if I set up an animation with a timed amount, all good. But doing this during a drag?
There is a way :)
As you've noticed, you can animate from a nil effect to an effect like UIBlurEffectStyleDark so if we add an animation and then pause the layer's animations we can control the progress of the effect by adjusting the layer's timeOffset!
- (void) setupBlur {
// Setup the blur to start with no effect
self.blurredEffectView = [[UIVisualEffectView alloc] initWithEffect:nil];
// Add animation to desired blur effect
[UIView animateWithDuration:1.0 animations:^{
[self.blurredEffectView setEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
}];
// Pause layer animations
self.blurredEffectView.layer.speed = 0;
}
Adjust blur between 0.0 and 1.0 (animation duration):
- (void) adjustBlur:(CGFloat)blurIntensity {
self.blurredEffectView.layer.timeOffset = blurIntensity;
}
Note: this won't work on iOS 8 where UIBlurEffect animations aren't supported.
I want to dissolve one UIImageView into another, but control the progress (so it occurs as the user pans their finger). This is simple with alpha, as I just change the UIView's alpha value as the user pans, but I don't know how to do it for a dissolve animation, which is slightly different from alpha changing.
Dissolve animation between two images can be made by placing one image on top of other and reducing alpha of the topmost image. This can be done in animation block.
In case of changing dissolve according to user's pan gesture can be written method like:
- (void)setDissolveState:(CGFloat)state
{
self.imageTop.alpha = 1.0 - state;
// ensure bottom image is opaque, this can be done once
self.imageBottom.alpha = 1.0;
}
Here imageTop and imageBottom are UIImage's properties of the object (for example of the view controller) who processes pan events. When change in pan is discovered the method setDissolveState: should be called with relative position of the touch:
CGFloat relativePosition = (touchLocation.x - self.view.frame.origin.x) / self.view.frame.size.width;
[self setDissolveState:relativePosition];
I am playing with the deferent animations using the alpha C4 framework.
I am trying to shape grow from the midel of the canvas till it covers it completely....Is there any animation that can create that zoom effect?
Yup, you can change and animate a C4Shape like this:
shape.animationDuration = 1.0f; //1 second animation duration
[shape rect:(-1,-1,769,1025)];
Note, the above is for a rectangle that will be 1px larger than the screen of an iPad on all sides. To make an ellipse you'd have to figure out the size of the frame that will encompass an ellipse that covers the whole screen.
I'm using
[UIView transitionWithView:duration:options:animations:completion:]
with option
UIViewAnimationOptionTransitionFlipFromRight
for a card flip, but the axis of rotation is the centre of the card. Is it possible to change that axis of rotation to the left edge?
This is not possible using this methods. If you want to do more tricky animation, you should use CoreAnimation explicit animations with 3D transform.
CoreAnimation programming guide
When you'll click to change the view you'll trigger the animation and then when the animation finished you change your views with no animation.
Let's say you have two cards
MyCardView *cardBack;
MyCardView *cardFront;
The shift method should be something like that :
-(void)switchView {
cardBack = [self methodToPrepareContentOfBackCard];
[self myCustomMethodToAnimateCardWithFlip];
}
When the animation is complete you'll use the delegate to set :
cardFront = [cardBack release];
[cardBack release];
I'd like to fill in a UIView Background with multiple colors. I want to use it as a status bar of sorts, so if 1/2 the necessary steps are completed, the UIView Background will be 1/2 green and 1/2 red. When the user completes more steps (say 2/3), more of the UIView Background turns green (2/3 in this case).
I'm guessing I need to override
-(void) drawREct: (CGRect) rect
I imagine I would get the UIView, figure out how big it is, and divide it into 2 rectangles and then fill in those rectangles.
Another option would be to add 2 UIViews programmatically, but I'm a fan of IB.
Is it possible to divy up a UIView like I want?
Thanks
This is not an IB solution, but you can subclass UIView and override the drawRect method to add a custom gradient. This will allow you to put any number of colors you like and have them transition hard or smoothly. There are many nice tutorials online that should different ways to do this (some more elaborate, some quite simple).
Another option that is fairly simple, is to override drawRect, make the background red, then fill a rectangle that takes up the bottom half of the view. It doesn't allow for fancy or smooth transitions between colors, but it's very easy to implement. For instance, something along these lines should work:
-(void)drawRect:(CGRect)rect {
CGRect upperRect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height * percentDone);
CGRect lowerRect = CGRectMake(rect.origin.x, rect.origin.y + (rect.size.height * percentDone), rect.size.width, rect.size.height *(1-percentDone));
[[UIColor redColor] set];
UIRectFill(upperRect);
[[UIColor greenColor] set];
UIRectFill(lowerRect);
}
Here percentDone is a float (declare, property(nonatomic), synthesize) that you can tie to the user's steps. Then just update the view when the user does something by
splitView.percentDone = .5;
[splitView setNeedsDisplay];
You can smooth this out with animations as well.
An easy way would be to set the background color for your background view to, say, red. Then add another view to that background view and call that the indicator view. Size and position the indicator view to cover the background, and set its background color to green. Connect the indicator view to an outlet in your view controller, and have the view controller adjust its width (or height) as necessary to correspond with the progress of the task at hand.
Another way would be as #PengOne suggests: create a custom view, give it a 'progress' property, and override -drawRect: to draw the contents appropriately. If you're going this route, there's nothing to stop you from getting a little creative. Instead of just filling two rectangles, make the boundary between the two colors a little more interesting. You could add ripples or bubbles that, with an appropriate sound effect, might look like a container filling with liquid. Or you could do a Qix-like animation that slowly fills the screen... ;-)