I was trying to mimic the yahoo weather app screen transition between cities, I couldn't figure out what transition it is. Any clue?
I really appreciate you time.
Thanks
Edit:
I have slideView ctrler which has a subview. The sliderview has an image and the subview has text. When I make a swipe, the text view with text must be moving and dragging way the view ctrler with it at a slower rate and this intern should start dragging in the next view ctrler which is an instance of slider Ctrler.
There is no built-in transition that does this for you (I assume you're talking about the images that are transitioning their frame/center at a different rate than the view itself). You'd probably have to write it yourself. Some basic familiarity with gesture recognizers and view animation is needed.
The basic effect is by simultaneously adjusting the center property for two image views as you change the frame of those views (or their super views). (Or, you can achieve this by having image views whose contentMode is UIViewContentModeCenter and just changing the frame.) I'd suggest you start with some simple tests of the effect and build from there.
For example, I created a scene that has two image views, whose autolayout constraints were defined as follows:
H:|[leftImageView][rightImageView]|
V:|[leftImageView]|
V:|[rightImageView]|
I then defined a width constraint for the leftImageView, and hooked it up to an IBOutlet for that constraint, e.g. leftImageWidthConstraint. I then have a UIPanGestureRecognizer that could handle the gesture, simply changing this leftImageWidthConstraint accordingly (and with auto layout, the rest of the frame is calculated automatically for me from that):
- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
CGPoint translate = [gesture translationInView:gesture.view];
static CGFloat width;
if (gesture.state == UIGestureRecognizerStateBegan)
{
width = self.leftImageWidthConstraint.constant;
}
CGFloat newWidth = width + translate.x;
if (newWidth < 0)
newWidth = 0;
else if (newWidth > self.view.bounds.size.width)
newWidth = self.view.bounds.size.width;
self.leftImageWidthConstraint.constant = newWidth;
// if you let go, animate the views to their final position
if (gesture.state == UIGestureRecognizerStateEnded)
{
// if more than half way, set left view's target width to take up full width,
// otherwise set left view's target width to zero
if (newWidth > (self.view.bounds.size.width / 2.0))
newWidth = self.view.bounds.size.width;
else
newWidth = 0;
// animate the changing of the constraint (and thus the `frame`) accordingly
self.leftImageWidthConstraint.constant = newWidth;
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.view layoutIfNeeded];
} completion:nil];
}
}
Thus, as I pan across, the two images are centered within their clipped frames:
This is a very basic implementation of the idea. There are, though, a ton of implementation details (custom container vs subviews, autolayout vs not, etc.), so until you answer some of those questions, it's going to be hard to be more specific.
It is not default but i achieved similar to it in my old app
Register gesture on your view and on detection set isFromLeftSide accordingly
Call following. do fine tune this as per your requirements
[self.view addSubview:mySlidingView];
mySlidingView.frame = // set offscreen frame, in the direction you want it to appear from depending on flag isFromLeftSide
[UIView animateWithDuration:8.0
animations:^{
mySlidingView.frame = // desired end location
}];
Related
I've a UIScrollView with other subviews inside an UIImageWiew and I need to rotate the whole content, so I take this road:
imageView.transform = CGAffineTransformMakeRotation([self.orientation floatValue]);
Good, works perfectly.
Being the ImageView inside a scroll view, I also need to set zoomScale in order to resize image inside, and I do it in this way:
- (void)updateZoom {
const float minZoom = MIN(self.view.bounds.size.width / self.imageView.image.size.width,
self.view.bounds.size.height / self.imageView.image.size.height);
if (minZoom > 1) {
return;
}
self.scrollView.minimumZoomScale = minZoom;
self.scrollView.zoomScale = minZoom;
}
updateZoom has the effect to "reset" initial transformation, so image come back to original orientation.
Generally, each time I modify "zoomScale" property, orientation is restored.
How can I keep both orientation both zoomScale?
I suppose I need to do something in scrollView delegate:
- (void)scrollViewDidZoom:(UIScrollView *)scrollView;
I just put a repo on GitHub that might help you. It is in Swift but it should do
PhotoSlideShow-Swift
I am building a uicollectionview that will have rows of cells. Each cell will contain a UIButton. I want to be able to 'zoom into' the UIButton on tap.
I have discovered this code for zooming in on self.view, but I am not sure how to make it properly zoom in on the center of the tapped UIButton. Right now, it zooms in towards the bottom right corner of the screen.
Any suggestions on properly centering in on the tapped button?
-(void)didTapButton:(UIButton*)sender{
CGFloat s = 3;
CGAffineTransform tr = CGAffineTransformScale(self.view.transform, s, s);
CGFloat h = sender.frame.size.height;
CGFloat w = sender.frame.size.width;
[UIView animateWithDuration:2.5 delay:0 options:0 animations:^{
self.view.transform = tr;
self.view.center = CGPointMake(w*s/2,h-h*s/2);
} completion:^(BOOL finished) {}];
}
If you're using auto layout, you won't be able to use view animation in the way you're doing, because layout is triggered by the transform and keeps resetting the frame. This could explain why the zoom seems to expand the view down and to the right rather than from the center. (You might like to see my essay on the struggle between auto layout and view transforms.)
I'm having an annoying problem with my UIView animation. Basically I'm doing a UIView rotation animation by following the finger position. Everything is working fine when the view is fully loaded but if the user drags his finger while new data (view content) is incoming from my web service the view is jumping off the screen. When the users scrolls a bit the view jumps back into it's position and everything is fine. So what can I do to prevent this jumping.
I think the incoming data triggers something like layoutIfNeeded and this effects the animation position.
Another idea is that it clashes with my autoLayout constraints.
The animation is done like this:
- (IBAction)handlePan:(UIPanGestureRecognizer *)gesture {
UIView *currentView = self.viewToAnimate;
CGPoint translation = [gesture translationInView:gesture.view.superview];
CGFloat percent = translation.x / gesture.view.bounds.size.width;
CATransform3D transform = CATransform3DIdentity;
transform.m34 = 1.0 / -800;
currentView.layer.transform =
CATransform3DRotate(transform, M_PI * percent, 0.0, 1.0, 0.0);
//Snap back if the users lifts his finger
if (gesture.state == UIGestureRecognizerStateEnded ||
gesture.state == UIGestureRecognizerStateCancelled ||
gesture.state == UIGestureRecognizerStateFailed) {
[UIView animateWithDuration:0.45
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{ currentView.transform = CGAffineTransformIdentity; }
completion:nil];
}
}
There is way too little information on what and how you are doing to solve this directly. What type are these views of, are you generating views programatically...
Most likely what is going on is one of 2 scenarios.
First the view itself is calling self to refresh in which case it sets its transform ti identity. If this is the case all you need to do is add this view to another superview and apply transform to that superview rather then the view itself.
Second possibility is the superview is calling the view to reposition. In this case you might want to break such operations by possibly setting some parameters on one of the views for this to stop doing automatically or try to analyse where this call comes from.
To analyse what is calling this view to reposition I suggest you to subclass your view and if needed even the layer and override the methods such as setFrame, setTransform and add breakpoints there.
I managed to get it working with [self.viewToAnimate setTranslatesAutoresizingMaskIntoConstraints:YES];.
I think my constraints repositioned the view during the loading because the view was growing. After reducing the constraint priority in IB and setting translatesAutoresizingMaskIntoConstraints to YES everything is working as expected.
I'm pretty new to auto layout and I'm confused about how to animate views.
I read a lot, and I know you must hold to the constraints, edit it, and wrap the layoutIfNeeded in an UIView animation block.
But when it comes to do it, I'm a little lost. I'd love if someone could explain me how this animation is done for example.
I think it probably uses a UIPanGestureRecognizer to change the constant of the leading space to container constraint, but it probably uses UIDynamics (for the bounce effect at the right ?).
Well, similar behavior could be achieved with UIPanGestureRecognizer + [UIView animateWithDuration:animations:]. Yes, you set leading space constraint and change it according to UIPanGestureRecognizer state. Remember that you need to set final constraints only (define final position of a slider). Intermediate animation positions are calculated for you. For the slider we have default left position and activated middle position.
For view rotation we can use transform property of UIView.
Autolayout constraints in IB:
Setting animation options (UIViewAnimationOptionCurveEaseOut animation curve) could give a feel of bounce effect. UIPanGestureRecognizer code (omit instance variables declaration, because their names are self-explanatory):
- (IBAction)onPan:(UIPanGestureRecognizer*)sender
{
switch (sender.state) {
case UIGestureRecognizerStateBegan:
_startOffset = self.leadingSpace.constant;
_maxOffset = self.slider.superview.frame.size.width
- kHorizontalPadding
- self.slider.frame.size.width;
break;
case UIGestureRecognizerStateChanged: {
CGFloat offset = _startOffset + [sender translationInView:self.slider.superview].x;
offset = MIN(offset, _maxOffset);
self.leadingSpace.constant = offset;
break;
}
case UIGestureRecognizerStateEnded: {
CGFloat offset = _startOffset + [sender translationInView:sender.view.superview].x;
UIColor *bgColor = [UIColor lightGrayColor];
CGFloat rotation = 0;
if (offset < _maxOffset) {
offset = kHorizontalPadding;
}
else {
offset = (_maxOffset + kHorizontalPadding)/2;
bgColor = [UIColor redColor];
rotation = M_PI_2;
}
self.leadingSpace.constant = offset;
[UIView
animateWithDuration:.5
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
[self.slider layoutIfNeeded];
self.slider.backgroundColor = bgColor;
self.slider.transform = CGAffineTransformMakeRotation(rotation);
} completion:nil];
break;
}
default:
break;
}
}
Animation result with UIViewAnimationOptionCurveLinear (capture simulator):
Animation result with UIViewAnimationOptionCurveEaseOut (capture simulator):
UIDynamics
With UIDynamics things become more complicated. Good starting point is Ray Wenderlich UIKit Dynamics Tutorial.
For bouncing slider we could add following behaviors:
UIGravityBehavior which pulls a slider to start position. We need to change angle property to direct gravity force to the left.
UICollisionBehavior which defines left and right edges of allowed movements. translatesReferenceBoundsIntoBoundary property will be useful if we treat parent view as boundary. Also we need to add extra boundary to stop slider in the middle using addBoundaryWithIdentifier:fromPoint:toPoint (or bezier path).
UIDynamicItemBehavior to change elasticy and possibly resistance properties to configure bounce and acceleration respectively.
Possibly UIPushBehavior in conjunction with recognizer's velocityInView: to specify slider velocity when a user releases a slider
Possibly UISnapBehavior as an alternative to UIGravityBehavior
Problem: I have a pinch gesture recognizer on a View Controller which I'm using to scale an image nested inside the View Controller. The transform below works fine, except that the image is scaled from the upper left instead of the center. I want it to scale from the center.
Setup:
a UIImageView set to Aspect Fill mode (nested within a few views, origin set to center).
a UIPinchGestureRecognizer on the container View Controller
I verified:
anchorPoint for image view is (0.5, 0.5)
the center is moving after every transform
no auto layout constraints on the view or its parent (at least at build time)
Also, I tried setting center of the UIImageView after the transform, the change doesn't take effect until after the user is done pinching.
I don't want to center the image on the touch because the image is smaller than the view controller.
CGFloat _lastScale = 1.0;
- (IBAction)pinch:(UIPinchGestureRecognizer *)sender {
if ([sender state] == UIGestureRecognizerStateBegan) {
_lastScale = 1.0;
}
CGFloat scale = 1.0 - (_lastScale - [sender scale]);
_lastScale = [sender scale];
CGAffineTransform currentTransform = self.imageView.transform;
CGAffineTransform newTransform = CGAffineTransformScale(currentTransform, scale, scale);
[self.imageView setTransform:newTransform];
NSLog(#"center: %#", NSStringFromCGPoint(self.imageView.center));
}
Here's a complete project demonstrating the issue.
https://github.com/adamloving/PinchDemo
no auto layout constraints on the view or its parent (at least at build time)
If your nib / storyboard uses auto layout, then there are certainly auto layout constraints, even if you did not deliberately construct them. And let's face it, auto layout does not play nicely with view transforms. A scale transform should scale from the center, but auto layout is fighting against you, forcing the frame to be reset by its top and its left (because that is where the constraints are).
See my essay on this topic here:
How do I adjust the anchor point of a CALayer, when Auto Layout is being used?
See also the discussion of this problem in my book.
In your case, the simplest solution is probably to apply the transform to the view's layer rather than to the view itself. In other words, instead of saying this sort of thing:
self.v.transform = CGAffineTransformMakeScale(1.3, 1.3);
Say this sort of thing:
self.v.layer.transform = CATransform3DMakeScale(1.3, 1.3, 1);