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.
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 understand the various ways of detecting drag gestures just fine (currently I'm using a UIPanGestureRecognizer), but my understanding of transformations is limited, so I'm not sure if/how this is possible. Essentially, what I want to have is a scaling transformation applied to a UIView at the same pace (for lack of a better word) as the user performs a drag gesture elsewhere on the screen. In other words, as the user drags up, I want the size of my transforming UIView to increase proportionally to the position of that gesture, and if the user then starts dragging down, it should start to shrink proportionally.
Hopefully that makes sense. As a dummy example, just imagine a slider that you can adjust to change the size of a UIView in real time. Is there a good way to make those sorts of incremental and constant size updates with CGAffineTransform?
In your pan gesture handler, you simply grab translationInView or locationInView, calculate a scale from that, and then update the transform accordingly. For example:
- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
static CGAffineTransform originalTransform;
if (gesture.state == UIGestureRecognizerStateBegan)
{
originalTransform = self.viewToScale.transform;
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
CGPoint translation = [gesture translationInView:gesture.view];
CGFloat scale = 1.0 - translation.y / 160.0;
self.viewToScale.transform = CGAffineTransformScale(originalTransform, scale, scale);
}
}
You can play around with the scale calculation depending upon precisely what you want to do, but hopefully you get the idea.
Personally, I'd rather use the pinch gesture recognizer for resizing (it's a UI that users have been trained on, it gives you the scale factor right out of the box, etc.), but whatever works for you. If you did a pinch gesture recognizer, it might look like:
- (void)handlePinch:(UIPinchGestureRecognizer *)gesture
{
static CGAffineTransform originalTransform;
if (gesture.state == UIGestureRecognizerStateBegan)
{
originalTransform = self.viewToScale.transform;
}
else if (gesture.state == UIGestureRecognizerStateChanged)
{
self.viewToScale.transform = CGAffineTransformScale(originalTransform, gesture.scale, gesture.scale);
}
}
I found the best approach is to use locationInView so that you can equate a location offset in pixels with a scale. For example, if the circle is placed in the centre of the view:
func dragDot(recognizer: UIPanGestureRecognizer) {
let locationX = recognizer.location(in: self.view).x
// Expand and contract the circle
let locationXOffset = locationX - self.view.center.x
// We need scale to be 1 when locationXOffset = circle radius
let scale: CGFloat = locationXOffset / (self.widthOfCircle / 2)
self.ring.transform = CGAffineTransform(scaleX: scale, y: scale)
}
If the circle isn't in the centre of the view then replace self.view.center.x with the initial location of the circle.
This method will work across all devices and screen resolutions and will avoid the requirement to calibrate a constant
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
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];
I have a UIView that I rotate with this code:
helpView.transform = CGAffineTransformMakeRotation(degreesToRadians( rotationAngle ));
Where degreeToRadians just is a macro to convert from degrees to radians.
This works fine as long as the view is visible, eg alpha = 1. When I hide it (alpha = 0, which I animate) it does not rotate any more. I guess this is a smart way for the devices to "save" on drawing time, but is there any way I can force it to be drawn even when alpha is 0? Otherwise I will have to rotate it before I show it again.
Any good ideas?
Thanks
Edit: This is the code I use to show/hide the view.
-(void)showHelp
{
bool helpAlpha = !helpView.alpha;
CGFloat newScale;
if (helpView.alpha) {
newScale = kHelpSmall;
helpView.transform = CGAffineTransformMakeScale(kHelpBig, kHelpBig);
} else {
newScale = kHelpBig;
helpView.transform = CGAffineTransformMakeScale(kHelpSmall, kHelpSmall);
}
[UIView animateWithDuration:(kAnimationTimeShort / 2) animations:^(void) {
[helpView setAlpha:helpAlpha];
helpView.transform = CGAffineTransformMakeScale(newScale, newScale);
}];
}
As you see I also scale it for a nicer effect. Works perfect when visible, does not rotate when alpha = 0. Rotation is done in another method, where I would prefer to keep it as I also rotate some other views there.
You are resetting the transform every time you use CGAffineTransformMake*. If you do this, you will get either a rotated transform or a scaled one. I am assuming the scaled one is after the rotated one and hence you aren't able to see the view rotated. If you need both the effects to remain, you will have to use CGAffineTransformRotate. So a scale and rotate will be
helpView.transform = CGAffineTransformMakeScale(kHelpSmall, kHelpSmall);
helpView.transform = CGAffineTransformRotate(helpView.transform, degreesToRadians(rotationAngle));
The order might vary.