Reputation: 25254
everybody knows the draggable view in the lockscreen of the camera
i want something similar. my layout looks like this
you can see this behavior pretty good on the lock screen (except the height stop).
i tried different things like UIPanGestureRecognizer
and then setting a max value but then i'm not able to slide.
is there something prepackaged in UIKit or have i to do it myself. if not, what would be a good aproach for this behavior?
Upvotes: 2
Views: 654
Reputation: 4897
Here's the plan: You're going to subclass UIView. You're going to add a few properties and override a few methods. The idea is that you let the finger dictate the movement with touchesBegan and touchesMoved. When the finger lifts in touchesEnded, you animate your view into a suitable resting position. In the code sample I'm providing, this will be either fully extended or fully retracted, but you could always have it retract if you so desired.
It sounds like you want a bit of inertia applied. That's slightly more complicated, because you have to add an extra animation yourself - but again, you just need to plug in the correct animation (e.g., have it animate out past the ending point and then back in, using a timing function) and have it fire after the user has finished moving. If you want to be fancy, you'd track the speed of the swipe and fire the animation based on this speed/momentum.
Following is a code sample that helps animate a drawer in or out. It doesn't have the fanciness you've described, but it should give you a basis to figure out how and where to introduce that kind of behavior.
You need a few properties/ivars on your class: A "current point", an origin, a ceiling (the point the view should cease dragging up past, or where it rests in "out", and a floor (the maximum y offset, or the coordinate it won't go below).
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint startPt = [touch locationInView:self];
self.currentPoint = startPt;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint activePoint = [[touches anyObject] locationInView:self];
//new position
CGPoint newPoint = CGPointMake(self.center.x,
self.center.y + (activePoint.y - currentPoint.y));
if (newPoint.y > self.draggingFloor) {
//too low
newPoint.y = self.draggingFloor;
} else if (newPoint.y < self.draggingCeiling) {
//too high
newPoint.y = self.draggingCeiling;
}
self.center = newPoint;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
//The user isn't touching. Now we can adjust to drawer to a "better" position.
//The 150f here is going to depend on how far the view should be dragged before it opens.
CGFloat median = pullDrawerYOrigin + 150.0f;
CGFloat position = self.center.y;
if (position <= median) {
//We're closer to open than closed. So open all the way.
[self animateDrawerToPositionYesForOpen:YES];
}
else if (position >= median) {
//close
[self animateDrawerToPositionYesForOpen:NO];
}
}
- (CGFloat) draggingFloor {
//Don't drag any lower than this.
if (!__draggingFloor) {
__draggingFloor = pullDrawerYOrigin + (self.bounds.size.height *.5);
}
return __draggingFloor;
}
- (CGFloat) draggingCeiling {
//Don't drag any higher than this.
if (!__draggingCeiling) {
__draggingCeiling = self.superview.bounds.size.height + (self.bounds.size.height * .05);
}
return __draggingCeiling;
}
- (void) animateDrawerToPositionYesForOpen:(BOOL)position {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[UIView setAnimationDuration:.2];
CGPoint myCenter = self.center;
if (position == YES) {
myCenter.y = self.draggingCeiling;
}
else if (position == NO) {
myCenter.y = self.draggingFloor;
}
self.center = myCenter;
[UIView commitAnimations];
}
Upvotes: 6