I've been battling with this for about an hour. I am trying to have my UIView to pans the same way the iOS Facebook app pans the main UIView and shows a navigation table at the left. So when you swipe to the right, it will pan all the way to the right and show you the navigation table. You can slide it back to the left. So it's basically like a slider.
I have a UIPanGestureRecognizer assigned to the UIView. And here is my selector for the gesture:
- (void)swipeDetected:(UIPanGestureRecognizer *)recognizer
{
CGPoint newTranslation = [recognizer translationInView:self.view];
NSLog(#"%f", newTranslation.x + lastTranslation.x);
// only pan appropriately when view is within correct bounds
if (lastTranslation.x + newTranslation.x >= 0 && lastTranslation.x + newTranslation.x <= 255)
{
self.navController.view.transform = CGAffineTransformMakeTranslation(newTranslation.x, 0);
if (recognizer.state == UIGestureRecognizerStateEnded) {
// if navcontroller is at less than 145px, snap back to 0
if (newTranslation.x + lastTranslation.x <= 145)
self.navController.view.transform = CGAffineTransformMakeTranslation(0, 0);
// else if its at more than 145px, snap to 255
else if (newTranslation.x + lastTranslation.x >= 145)
self.navController.view.transform = CGAffineTransformMakeTranslation(255, 0);
lastTranslation.x += newTranslation.x;
}
}
}
This works perfectly when sliding the UIView to the right. It will then stick at 255px so a part of it is shown on the screen, and so it does not disappear. However, when it's at the position, when I slide it back to the left, it jumps all the way to the origin instead of following the pan gesture.
Why is that? How can I fix it?
Thanks
Related
Im using the UIGestureRecognizer to transform a view and its workin perfectly, but now I want to use it to transform my view width and height independently. The only way that comes to my mind to solve this is getting the two finger positions and make an if clause to recognize if the user is trying to increase width or height, but for this I need to get each finger position involved in the Pinch Gesture. But I cant find any method to do this I was wondering if this is posible or if there is another alternative for achieving this.
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer {
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, 1);//To transform height insted of width just swap positions of the second and third parameter.
NSLog(#"%f",recognizer.scale);
recognizer.scale = 1;
}
Got the answer if somebody needs to do this, there is a method called location of touch. With this method you can get each touch x and y position. But call it when the Gesture recognizer state began because it crashes if you do it in the state changed. Save this values in some variables and you are good to go. Hope it helps someone who is interested.
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer {
if(recognizer.state == UIGestureRecognizerStateBegan){
NSLog(#"pos : 0%f, %f",[recognizer locationOfTouch:0 inView:self.view].x,[recognizer locationOfTouch:0 inView:self.view].y);
NSLog(#"pos 1: %f, %f",[recognizer locationOfTouch:1 inView:self.view].x,[recognizer locationOfTouch:1 inView:self.view].y);
}
if(recognizer.state == UIGestureRecognizerStateChanged){
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, 1);
//NSLog(#"%f",recognizer.scale);
recognizer.scale = 1;
}
if(recognizer.state == UIGestureRecognizerStateEnded){
}
I'd like to let the user control the brightness in an iOS app using a gesture recogniser to swipe up and down. Can gestures even control brightness? I've done it before using a slider and the brightness property of UIScreen...
[UIScreen mainScreen].brightness = 0.5;
I want to be able to control the brightness as you would with a slider (like the iBooks app) but instead of a slider use a gesture recogniser. The idea is that the gesture continuously changes the brightness value as long as a touch is being recognised and the finger moves either up or down. I wasn't sure if I should use a pan gesture and limit the direction somehow to up or down, or use a swipe gesture (but obviously that detects one swipe and doesn't continuously update a value).
Perhaps moving a finger up a specified amount on the screen increments the 0.0 - 1.0 range of the brightness value. I'm not sure how to approach it so i'm just thinking out loud.
Any ideas? Thanks a lot.
EDIT: Ok so I found this code on another similar question
- (void)pan:(UIPanGestureRecognizer *)recognizer
{
if ((recognizer.state == UIGestureRecognizerStateChanged) ||
(recognizer.state == UIGestureRecognizerStateEnded))
{
CGPoint velocity = [recognizer velocityInView:self.view];
if (velocity.y >0) // panning down
{
self.brightness = self.brightness -.02;
NSLog (#"Decreasing brigntness in pan");
}
else // panning up
{
self.brightness = self.brightness +.02;
NSLog (#"Increasing brigntness in pan");
}
}
}
It seems to at least print an NSLog every time you gesture up or down, I guess I now just want to know if there's a way to use NSLog to find out the current brightness value rather than it just telling you if it detected a gesture (unless i'm being stupid and the NSLog i'm using already tells me that?). Thanks, this would be really useful to know because I can't run on the device. :-D
To Increase and decrease the brightness of the Screen
For Swift
1)How to Add PanGesture in View
let panGesture = UIPanGestureRecognizer.init(target: self, action: #selector(pan(recognizer:)))
panGesture.delegate = self
panGesture.minimumNumberOfTouches = 1
mp.view.addGestureRecognizer(panGesture)
2)Code to increase and Decrease the Brightness of Screen using PanGesture
func pan(recognizer:UIPanGestureRecognizer){
if recognizer.state == UIGestureRecognizerState.changed || recognizer.state == UIGestureRecognizerState.ended {
let velocity:CGPoint = recognizer.velocity(in: self.view)
if velocity.y > 0{
var brightness: Float = Float(UIScreen.main.brightness)
brightness = brightness - 0.03
UIScreen.main.brightness = CGFloat(brightness)
print("Decrease brigntness in pan")
}
else {
var brightness: Float = Float(UIScreen.main.brightness)
brightness = brightness + 0.03
UIScreen.main.brightness = CGFloat(brightness)
print("Increase brigntness in pan")
}
}
}
Yes! Just use a UIPanGestureRecognizer and adjust the brightness value accordingly as the pan gesture is called. Check the Apple docs.
I added a -(void) detectTouch: (UIPanGestureRecognizer *) event for UIScrollView and detecting the angle on which user is moving his finger. My task is to scroll the UIScrollView horizontally only when user is moving the finger between 0 - 30 degrees (just to make sure he is drawing a horizontal straight line) otherwise I have to disable the UIScrollView scroll.
I am detecting the angle by drawing a triangle using the touch starting point and ending point.
Problem: I enabled the UIScrollView scroll when the angle is < 30 degrees but this is not working on the first time. Although I enabled scroll using scrollEnabled = YES it is working only when user is stopped touching the screen (taking the finger from the screen).
The following code I used to
- (void)viewDidLoad
{
[super viewDidLoad];
[self PanGesture:self.view callBack:#selector(detectTouch:) delegate:self];
incrementer = 0;
}
-(void) detectTouch: (UIPanGestureRecognizer *) event{
// Calculating point A on gesture starts
if(event.state == UIGestureRecognizerStateBegan){
pointA.x = fabs([event translationInView:event.view].x);
pointA.y = fabs([event translationInView:event.view].y);
NSLog(#"A: %f, %f", pointA.x, pointA.y);
}
incrementer += 1;
// Start calculating Point B, Point C on calling this function 3 times
if(incrementer >= 3){
// Calculating point C
pointC.x = fabs([event translationInView:event.view].x);
pointC.y = fabs([event translationInView:event.view].y);
NSLog(#"C: %f, %f", pointC.x, pointC.y);
// calculate pointB using A, C
pointB.x = fabs(pointC.x);
pointB.y = fabs(pointA.y);
NSLog(#"B: %f, %f", pointB.x, pointB.y);
float X = pointB.x - pointA.x;
float Y = pointC.y - pointB.y;
float angle = (atan(fabs(Y) / fabs(X)) * 180 / M_PI);
if(angle > 30){
// This disable is not working on while user is moving the finger
self.myScrollView.scrollEnabled = NO;
NSLog(#"UIScroll Disabled");
}else{
// This enable is not working on while user is moving the finger
self.myScrollView.scrollEnabled = YES;
NSLog(#"UIScroll Enabled");
}
incrementer = 0;
}
}
How can I enable UIScrollView scroll while user is moving the touch?
Since UIScrollView seems to accept scrolling only with a new touch after you set .scrollEnabled to YES, I would do the following:
set .scrollEnabled to NO
Track the touch (e. g. with touchesMoved::)
check whether you 30 degrees condition is met
use the movement to adjust UIScrollView.contentOffset
I'm trying to create a knob-like behavior in one of my views with a UIRotationGestureRecognizer. This works, and positions the view as expected. However, every single time a gesture is performed, the rotation of the recognizer resets, so the knob starts at 0 every time.
How can I remember the last rotation of the UIRotationGestureRecognizer to let the user adjust the knob UIView without resetting it every single time?
I'm trying to make the recognizer start calculating rotation changes from the view's last known rotation:
knob starts at 0, recognizer is at 0
recognizer is rotated to 45 degrees
recognizer stops rotating
the knob is left at 45 degrees //this is already happening with the provided code snippet
next touch:
//this is what's is happening
recognizer starts at 0, rotates the knob back to 0
//I want it to be:
recognizer starts at 45, rotates the knob as in the example above.
- (IBAction)rotateView:(id)sender {
if([sender isKindOfClass:[UIRotationGestureRecognizer class]])
{
UIRotationGestureRecognizer* recognizer = sender;
CGAffineTransform transform = CGAffineTransformMakeRotation([recognizer rotation]);
rotatingView.transform = transform;
}
}
You should be able to get the current rotation of the rotatingView from it's transform property. Store this value into a savedRotation variable when the gesture begins. Make sure to assign a delegate to handle the gestureRecognizerShouldBegin callback.
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)recognizer
{
savedRotation = atan2(rotatingView.transform.b, rotatingView.transform.a);
return YES;
}
- (void)rotateView:(UIRotationGestureRecognizer*)recognizer
{
rotatingView.transform = CGAffineTransformMakeRotation(recognizer.rotation+savedRotation);
}
Transform the transform:
rotatingView.transform = CGAffineTransformRotate(rotatingView.transform, [recognizer rotation]);
You have to make sure that you reset the rotation after the transform. Otherwise they will stack on top of each other and you get the "interesting" behavior.
rotatingView.transform = CGAffineTransformScale(rotatingView.transform, recognizer.scale, recognizer.scale);
[recognizer setRotation:0]; // this line
You should also do this for any translation or scaling transformations that you may do when handling gestures. The methods there are:
[recognizer setScale:1];
[recognizer setTranslation:CGPointZero inView:recognizer.view.superview];
Edit: See the answer below.
I finally give up and come here to ask you for my problem...
I'm using a UIScrollView for a scrolling menus with little icons.
On each page, with paging enabled, there's an icon in the center, and 2 and a half other visible icons on the left and right. I can move from one icon to its neighbour, and that is fine, but the point is that if I do a fast scrolling, it will not move from more than 3 icons, which is the width of the screen.
What I would want is to be able to scroll on more than 3 icons, and that the magnet behaviour is only triggered when it's slowing down.
I've tried to schedule the scroll view to calculate its velocity, and set the pagingEnabled attribute to NO when it's moving fast and YES again when it's slowing down, but as soon as it is set to YES, the view comes back very fast at its original position, as if it was not detecting that I had brought it to a new page. Would anyone know why it does this? And if I have a way to tell the view "ok, now the paging is enabled but look, you're 15 pages later. Just center on the current page, don't come back at the beginning."
Here's my update function (if it can help):
-(void)update:(ccTime)dt
{
float velocity = fabsf((self.previousOffset-self.scrollView.contentOffset.y)/dt);
self.previousOffset = self.scrollView.contentOffset.y;
CCLOG(#"Velocity: %f", velocity);
if(self.scrollView.pagingEnabled)
{
if(velocity > 100)
{
self.scrollView.pagingEnabled = NO;
}
}
else
{
if(velocity < 100)
{
self.scrollView.pagingEnabled = YES;
}
}
}
I finally found a solution, which was pretty obvious, but that I did not see at the beginning, by using setContentOffset on the scrollView.
Here is the new update function:
-(void)update:(ccTime)dt
{
float velocity = 1000;
if(self.previousOffset)
{
velocity = fabsf((self.previousOffset-self.scrollView.contentOffset.y)/dt);
}
self.previousOffset = self.scrollView.contentOffset.y;
if(velocity < 300)
{
CGSize screenSize = [[CCDirector sharedDirector] winSize];
float halfScreen = screenSize.width/2;
CCLayer *panel = (CCLayer *)[self getChildByTag:1];
SQScrollViewMenu *menu = (SQScrollViewMenu *)[panel getChildByTag:1];
SQMissionItem *currentItem = (SQMissionItem *)[menu getChildByTag:currentPage];
float contentOffsetY = [self.scrollView contentOffset].y;
CCLOG(#"Currentpage: %i ; currentoffsetY: %f", currentPage, contentOffsetY);
float distance = contentOffsetY + [currentItem position].x - halfScreen + panel.position.x + menu.position.x + panel.parent.position.x - 60;
CCLOG(#"Which gives a distance of: %f", distance);
[self.scrollView setContentOffset:CGPointMake(0, distance) animated:YES];
self.previousOffset = 0;
[self unschedule:#selector(update:)];
CCLOG(#"Is unscheduled");
}
}
And it is almost working... at least, it is on simulator. But as soon as I try it on my iPhone 4, it does not work anymore. It always go into that update function, but 7 times among 8, it just blocks the scrollView as it is and does not drags it back to the position I give it... but sometimes it does.
Would anyone have an idea? I found similar issues on the net, but none of them could resolve this...