How to set the slider to clicked position and get the slider value on the clicked location on UISlider in iPhone programming.
i know we can drag the slider to that position but i dont want to do it. Can you please tel me how to set the slider to clicked position? Is this possible to do?
Here is the part "left as a user exercise":
- (void) tapped: (UITapGestureRecognizer*) g {
UISlider* s = (UISlider*)g.view;
if (s.highlighted)
return; // tap on thumb, let slider deal with it
CGPoint pt = [g locationInView: s];
CGFloat percentage = pt.x / s.bounds.size.width;
CGFloat delta = percentage * (s.maximumValue - s.minimumValue);
CGFloat value = s.minimumValue + delta;
[s setValue:value animated:YES];
}
The way I did it is to subclass the slider and check in touchesBegan. If the user taps on the thumb button area (which we track) then ignore the tap, but any where else on the trackbar we do:
:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self];
// if we didn't tap on the thumb button then we set the value based on tap location
if (!CGRectContainsPoint(lastKnownThumbRect, touchLocation)) {
self.value = self.minimumValue + (self.maximumValue - self.minimumValue) * (touchLocation.x / self.frame.size.width);
}
[super touchesBegan:touches withEvent:event];
}
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value {
CGRect thumbRect = [super thumbRectForBounds:bounds trackRect:rect value:value];
lastKnownThumbRect = thumbRect;
return thumbRect;
}
You can simply add a UITapGestureRecognizer to the slider, then pull out the UIEvent and Touches associated with it to figure out where along the UISlider the tap took place. Then set your slider's value to this calculated value.
UPDATE:
First, setup your slider and add the gesture recognizer to it.
UISlider *slider = [[[UISlider alloc] init] autorelease];
…
<slider setup>
…
UITapGestureRecognizer *gr = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(sliderTapped:)] autorelease];
[slider addGestureRecognizer:gr];
Then implement the selector
- (void)sliderTapped:(UIGestureRecognizer *)gestureRecognizer {
<left as a user excercise*>
}
*Hint: Read the docs to figure out how to get the locationInView extrapolated and figure out what the slider should be
It seems like just subclassing UISlider and returning always true to the beginTracking produce the desired effect.
iOS 10 and Swift 3
class CustomSlider: UISlider {
override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
return true
}
}
I was using the subclassed UISlider code to listen to ValueChanged events in ios6> in order to implement a snap-to-grid type of function.
This was broken when upgrading to iOS7 as it no longer fired the UIControlEventsValueChanged. For anyone having the same problem it is fixed by adding a line in the if() as below:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self];
// if we didn't tap on the thumb button then we set the value based on tap location
if (!CGRectContainsPoint(lastKnownThumbRect, touchLocation)) {
self.value = self.minimumValue + (self.maximumValue - self.minimumValue) * (touchLocation.x / self.frame.size.width);
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
[super touchesBegan:touches withEvent:event];
}
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value {
CGRect thumbRect = [super thumbRectForBounds:bounds trackRect:rect value:value];
lastKnownThumbRect = thumbRect;
return thumbRect;
}
Hope it helps someone :)
I have used this code. Get from: http://imagineric.ericd.net/2012/11/15/uislider-touch-to-set-value/
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(sliderTapped:)];
[slider addGestureRecognizer:gr];
- (void)sliderTapped:(UIGestureRecognizer *)g {
UISlider* s = (UISlider*)g.view;
if (s.highlighted)
return; // tap on thumb, let slider deal with it
CGPoint pt = [g locationInView: s];
CGFloat percentage = pt.x / s.bounds.size.width;
CGFloat delta = percentage * (s.maximumValue - s.minimumValue);
CGFloat value = s.minimumValue + delta;
[s setValue:value animated:YES];
}
Hope this can help you.
Related
I am trying to drag an image view. I have got little bit of success in doing so, but it is not behaving as i want. I wish that it should move only if touch inside the image and drag it.
But it is moving even if I am touching and dragging from anywhere on the screen.
I have written code like this:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//retrieve touch point
CGPoint pt= [[ touches anyObject] locationInView:[self.view.subviews objectAtIndex:0]];
startLocation = pt;
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint pt = [[touches anyObject] locationInView: [self.view.subviews objectAtIndex:0]];
CGRect frame = [[self.view.subviews objectAtIndex:0]frame];
frame.origin.x += pt.x - startLocation.x;
frame.origin.y += pt.y - startLocation.y;
[[self.view.subviews objectAtIndex:0] setFrame: frame];
}
The return value of locationInView method is point relative to the view frame. check it is or not in the view frame first.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGRect targetFrame = [self.view.subviews objectAtIndex:0].frame;
//retrieve touch point
CGPoint pt= [[ touches anyObject] locationInView:[self.view.subviews objectAtIndex:0]];
//check if the point in the view frame
if (pt.x < 0 || pt.x > targetFrame.size.width || pt.y < 0 || pt.y > targetFrame.size.height)
{
isInTargetFrame = NO;
}
else
{
isInTargetFrame = YES;
startLocation = pt;
}
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
if(!isInTargetFrame)
{
return;
}
//move your view here...
}
Try something like this:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//retrieve touch point
startLocation = [[ touches anyObject] locationInView:self.view];
// Now here check to make sure that start location is within the frame of
// your subview [self.view.subviews objectAtIndex:0]
// if it is you need to have a property like dragging = YES
// Then in touches ended you set dragging = NO
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint pt = [[touches anyObject] locationInView: [self.view.subviews objectAtIndex:0]];
CGRect frame = [[self.view.subviews objectAtIndex:0]frame];
frame.origin.x += pt.x - startLocation.x;
frame.origin.y += pt.y - startLocation.y;
[[self.view.subviews objectAtIndex:0] setFrame: frame];
I've added some UIImageView dynamically and filled it with different images, what I am trying to do is "Allow user to set position for any UIImageView", for that I used
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//Here I want the object of particular UIImageView on which user touched.
}
In that method I'm doing,
NSLog(#"%#",[touches anyObject]);
It returns output
<UITouch: 0x68b95e0> phase: Began tap count: 1 window: <UIWindow: 0x68875d0; frame = (0 0; 320 480); layer = <UIWindowLayer: 0x68b6470>> view: <UIImageView: 0x6a74cf0; frame = (83.7763 83.7763; 182.447 182.447); transform = [0.968912, -0.247404, 0.247404, 0.968912, 0, 0]; alpha = 0.8; opaque = NO; tag = 3; layer = <CALayer: 0x6a74980>> location in window: {161, 230} previous location in window: {161, 230} location in view: {52.7761, 105.448} previous location in view: {52.7761, 105.448}
Note, in above output, it showing my UIImageView object on which I touched. But I want that object from it!!!
I want my UIImageView on which user touched?, I have already set property userInteractionEnabled=YES so the problem isn't with it!
I used below code to get it so, but it wont work.
NSInteger tag=[[[touches anyObject] view] tag]; //It only returns tag of UIView tag
I Google it but doesn't come with solution!
Thank you in advance for any help!
Here you go:
this is only for one imageview you can detect the other by the same if statement.
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touch_point = [touch locationInView:self.view];
if (![imageView pointInside:touch_point withEvent:event])
{
NSLog(#"point inside imageview");
}
}
or you can also do this :p
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if (touch.view == iv)
{
NSLog(#"i got you");
}
}
like this: (iv and iv2 are 2 different UIImageView`s)
if (touch.view == iv)
{
NSLog(#"i got you");
}
if (touch.view == iv2)
{
NSLog(#"i got you too :p");
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch=[touches anyObject];
if([[touch valueForKey:#"view"] isKindOfClass:[UIImageView class]])
{
UIImageView *viewSelected=(UIImageView *)[touch valueForKey:#"view"]; //it returns touched object
//for further differences can user viewSelected.tag
}
}
Code to get only the X and Y coords from the UIImageView:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touch_point = [touch locationInView:self.imgView];
if ([imgView pointInside:touch_point withEvent:event])
{
NSLog(#"point inside imageview");
cords=[NSString stringWithFormat:#"%f,%f",touch_point.x,touch_point.y];
NSLog(#"cords are%#",cords);
}
}
You can use UIButton with Image properties instead of UIImageView.
If you would like to call your own function ,It's pretty easy to handle many event like Touch up inside or Touch Cancel by adding selector.
UIButton *yourBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
/* Depend on your dynamic programming handle */
yourBtn.frame = CGRectMake(40, 140, 240, 30);
/* If you prefer just only image ,no need to set Title name */
[yourBtn setTitle:#"Your Button Title" forState:UIControlStateNormal];
/* choose yourFunction for each UIButton */
[yourBtn addTarget:self action:#selector(yourFunction) forControlEvents:UIControlEventTouchUpInside];
/* your button will be appear in the position that you have define */
[self.view addSubview:yourBtn];
Hope It helps you!
1 Subclass UIImageView and implement:
Responding to Touch Events
– touchesBegan:withEvent:
– touchesMoved:withEvent:
– touchesEnded:withEvent:
– touchesCancelled:withEvent:
Responding to Motion Events
– motionBegan:withEvent:
– motionEnded:withEvent:
– motionCancelled:withEvent:
or:
2 Add UIGestureRecognizer to each UIImageView.
You could add the View that you add into an NSMutableArray and then just compare like this:
I am not in my mac, but It's something similar to this:
NSInteger viewID = [_views indexOfObject:[[touches anyObject] view]];
This return and number if not isn't exist do this:
if (viewID != NSNotFound) {
//it exist the view and its in the array
}
I was wondering if there is an easy method to set the minimum swipe length, i.e. the length in pixel the user needs to swipe so that the gesture is recognised as a swipe.
I noticed that the normal swipe can be quite unresponsive (as compared to swiping photos in your photo library for instance).
This is the normal way, but I would like to reduce the required length of the swipe:
- (void)viewDidLoad
{
// SWIPING GESTURES:
UISwipeGestureRecognizer *swipeLeftRecognizer;
swipeLeftRecognizer=[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(foundLeftSwipe:)];
swipeLeftRecognizer.direction=UISwipeGestureRecognizerDirectionLeft;
//swipeRecognizer.numberOfTouchesRequired=1;
[self.view addGestureRecognizer:swipeLeftRecognizer];
[swipeLeftRecognizer release];
UISwipeGestureRecognizer *swipeRightRecognizer;
swipeRightRecognizer=[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(foundRightSwipe:)];
swipeRightRecognizer.direction=UISwipeGestureRecognizerDirectionRight;
//swipeRecognizer.numberOfTouchesRequired=1;
[self.view addGestureRecognizer:swipeRightRecognizer];
[swipeRightRecognizer release];
[super viewDidLoad];
}
#pragma mark -
#pragma mark Swipes
- (void)foundLeftSwipe:(UISwipeGestureRecognizer *)recognizer {
// do something
}
- (void)foundRightSwipe:(UISwipeGestureRecognizer *)recognizer {
// do something
}
I remember that there is a way to get the pixel start position and end position and then compare the two, but was just wondering if there is a simpler method, i.e. by simply defining a value for the minimum required swipe length in the code I have here.
EDIT:
This is how I recoded the whole thing:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
gestureStartPoint = [touch locationInView:self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self.view];
CGFloat deltaXX = (gestureStartPoint.x - currentPosition.x); // positive = left, negative = right
CGFloat deltaYY = (gestureStartPoint.y - currentPosition.y); // positive = up, negative = down
CGFloat deltaX = fabsf(gestureStartPoint.x - currentPosition.x); // will always be positive
CGFloat deltaY = fabsf(gestureStartPoint.y - currentPosition.y); // will always be positive
if (deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance) {
if (deltaXX > 0) {
label.text = #"Horizontal Left swipe detected";
[self performSelector:#selector(eraseText) withObject:nil afterDelay:2];
}
else {
label.text = #"Horizontal Right swipe detected";
[self performSelector:#selector(eraseText) withObject:nil afterDelay:2];
}
}
if (deltaY >= kMinimumGestureLength && deltaX <= kMaximumVariance) {
if (deltaYY > 0) {
label.text = #"Vertical up swipe detected";
[self performSelector:#selector(eraseText) withObject:nil afterDelay:2];
}
else {
label.text = #"Vertical down swipe detected";
[self performSelector:#selector(eraseText) withObject:nil afterDelay:2];
}
}
}
I was facing the exact same thing, and I was looking for a solution. There is no such thing as required distance in UISwipeGestureRecognizer but that's not all you can use. I ended up using UIPanGestureRecognizer! It looks like a super class of swipe gesture recognizer and it certainly exposes more useful data.
There is a method available in this class called translationInView:(UIView *)aView the returns a CGPoint where x & y is the total distance over the whole pan gesture! something amazingly useful for our request! And it respects the direction by positive and negative values, meaning: y=+100 -> swipe up to down 100 points, x=+100 swipe left to right 100 points, etc...
What am I doing is to check if the user has panned for a given amount of points (I was looking for something like swipe down, so for me it was y>150) and then do my thing...
here is some code excerpts:
-(void)viewDidLoad
{
UIPanGestureRecoginzer *panDown=[[UIPanGestureRecoginzer alloc] initWithTarget:self action:#selector(swipedDown:)];
[self.view addGestureRecognizer:panDown];
}
.
.
.
-(void)swipedDown:(UIPanGestureRecoginzer *)recognizer
{
CGPoint panned=[recognizer translationInView:self.view];
if(panned.y>150){
//swiped down for 150 points
doSomething();
}
}
It also gives you the velocity of the pan: velocityInView:
Note: You should use your superview (self.view in most cases) if you want to have an overall pan effect.
Hope this helps: UIPanGestureRecognize Class Reference
Please see this documentation http://developer.apple.com/library/ios/#documentation/uikit/reference/UIResponder_Class/Reference/Reference.html
You can use touchesBegan, touchesMoved, and touchesEnded methods to find the start/end positions of the swipe. By finding the delta between the start and the end, you can get the swipe length.
Maybe something like this.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
UITouch *touch = [touches anyObject];
gestureStartPoint = [touch locationInView:self.view];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint gestureEndPoint = [touch locationInView:self.view];
// compare gestureStartPoint and gestureEndPoint to determine swipe length
}
Check out this question: UISwipeGestureRecognizer Swipe length
Swift 3:
var gestureStartPoint: CGPoint = .zero
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
gestureStartPoint = touch!.location(in: self.view)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let gestureEndPoint = touch!.location(in: self.view)
let dist = distance(gestureStartPoint, gestureEndPoint)
print(dist)
}
func distance(_ a: CGPoint, _ b: CGPoint) -> CGFloat {
let xDist = a.x - b.x
let yDist = a.y - b.y
return CGFloat(sqrt((xDist * xDist) + (yDist * yDist)))
}
I have a project where a UITextView (for multilines) can be dragged around the screen. So far my solution to this has been an overlay of an invisible UIButton which when dragged its center is the same as the UITextView's center.
However I've seen apps that seem to just allow the UITextView to be dragged and edited on the fly so it seems there might not be an overlay in those but I'm not sure.
Thoughts?
By the way, c in this code is the UIButton and this is how I have moved it thus far:
- (void) draggedOut: (UIControl *) c withEvent: (UIEvent *) ev
{
if(self.interfaceOrientation == UIInterfaceOrientationPortrait)
{
c.center = [[[ev allTouches] anyObject] locationInView:self.view];
AddedText.center = c.center;
}
else if(self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
c.center = [[[ev allTouches] anyObject] locationInView:self.view];
AddedText.center = c.center;
}
else if(self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
{
c.center = [[[ev allTouches] anyObject] locationInView:self.view];
AddedText.center = c.center;
}
else if(self.interfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
c.center = [[[ev allTouches] anyObject] locationInView:self.view];
AddedText.center = c.center;
}
}
- (void)panTextView:(UIPanGestureRecognizer *)recognizer {
NSLog(#"panning");
location1 = [recognizer translationInView:draggableTextView];
recognizer.view.center = CGPointMake(recognizer.view.center.x + location1.x,
recognizer.view.center.y + location1.y);
[recognizer setTranslation:CGPointMake(0,0) inView:draggableTextView];
location1 =[recognizer locationInView:draggableTextView];
NSLog(#"tranlation %#",NSStringFromCGPoint(location1));
[_imgpic addSubview:recognizer.view];
appDelegate.txt=draggableTextView.text;
}
call this method after creating textview.
Well have not been able to manipulate the actual uitextview.
First tried making a button overlay that could be moved and could be pressed to start editing, but it wasn't centered properly.
Then tried the above method to move the UITextView itself. But it would only work on touches or drags. (Note this was a modified form of touchesBegan & touchesMoved)
Ended up with a UIScrollView with the UITextView as a subview. Now it can move smoothly just that it can be moved from any place on the screen. Not optimal but is the best result to thus keep everything else intact.
Does the textView need to support scrolling? If so, this could get complicated.
But if not, there are two approaches. 1) subclass the textview and override touchesBegan, touchesMoved, touchesEnded. 2) write a gesture recognizer that processes the same messages and attach it to the textview.
Here's an example of a Gesture recognizer that will do the job:
#interface TouchMoveGestureRecognizer : UIGestureRecognizer
{
CGPoint _ptOffset;
}
#end
#implementation TouchMoveGestureRecognizer
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* t = [touches anyObject];
_ptOffset = [t locationInView: self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* t = [touches anyObject];
CGPoint pt = [t locationInView: self.view.superview];
pt.x -= _ptOffset.x;
pt.y -= _ptOffset.y;
CGRect r = self.view.frame;
r.origin = pt;
self.view.frame = r;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
_ptOffset = CGPointMake(-1, -1);
}
#end
and, how to use it:
- (void)viewDidLoad {
[super viewDidLoad];
_textView.scrollEnabled = NO;
TouchMoveGestureRecognizer* gr = [[[TouchMoveGestureRecognizer alloc] init] autorelease];
[_textView addGestureRecognizer: gr];
}
Fairly common question this, to which I have a few answers and I'm nearly there. I have a button which when pressed, will create an image (code as follows)
(numImages is set on load to ZERO and is used as a count up for the tag numbers of all images created)
UIImage *tmpImage = [[UIImage imageNamed:[NSString stringWithFormat:#"%i.png", sender.tag]] retain];
UIImageView *myImage = [[UIImageView alloc] initWithImage:tmpImage];
numImages += 1;
myImage.userInteractionEnabled = YES;
myImage.tag = numImages;
myImage.opaque = YES;
[self.view addSubview:myImage];
[myImage release];
I then have a touchesBegan method which will detect what's touched. What I need it to do is to allow the user to drag the newly created image. It's nearly working, but the image flickers all over the place when you drag it. I can access the image which you click on as I can get it's TAG, but I just cannot drag it nicely.
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
if (touch.view.tag > 0) {
touch.view.center = location;
}
NSLog(#"tag=%#", [NSString stringWithFormat:#"%i", touch.view.tag]);
}
- (void) touchesMoved:(NSSet *)touches withEvent: (UIEvent *)event {
[self touchesBegan:touches withEvent:event];
}
It works, in that I get an output of the tag for each image as I click on them. But when I drag, it flashes... any ideas?
In answer to my own question - I decided to create a class for handling the images I place on the view.
Code if anyone's interested....
Draggable.h
#import <Foundation/Foundation.h>
#interface Draggable : UIImageView {
CGPoint startLocation;
}
#end
Draggable.m
#import "Draggable.h"
#implementation Draggable
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
// Retrieve the touch point
CGPoint pt = [[touches anyObject] locationInView:self];
startLocation = pt;
[[self superview] bringSubviewToFront:self];
}
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
// Move relative to the original touch point
CGPoint pt = [[touches anyObject] locationInView:self];
CGRect frame = [self frame];
frame.origin.x += pt.x - startLocation.x;
frame.origin.y += pt.y - startLocation.y;
[self setFrame:frame];
}
#end
and to call it
UIImage *tmpImage = [[UIImage imageNamed:"test.png"] retain];
UIImageView *imageView = [[UIImageView alloc] initWithImage:tmpImage];
CGRect cellRectangle;
cellRectangle = CGRectMake(0,0,tmpImage.size.width ,tmpImage.size.height );
UIImageView *dragger = [[Draggable alloc] initWithFrame:cellRectangle];
[dragger setImage:tmpImage];
[dragger setUserInteractionEnabled:YES];
[self.view addSubview:dragger];
[imageView release];
[tmpImage release];
Usually you get an implicit animation when you change center. Are you messing with -contentMode or calling -setNeedsDisplay by any chance?
You can explicitly request animation to avoid the delete and re-draw this way:
if (touch.view.tag > 0) {
[UIView beginAnimations:#"viewMove" context:touch.view];
touch.view.center = location;
[UIView commitAnimations];
}
Do note that NSLog() can be very slow (much slower than you'd expect; it's much more complicated than a simple printf), and that can cause trouble in something called as often as touchesMoved:withEvent:.
BTW, you're leaking tmpImage.