draw a vertical line where the screen was tapped - ios

I want to draw a vertical line on the place where the screen was tapped. Because the average finger is wider than 1 pixel wide I want to do this "in steps". So basically, the line can only be drawn every 25px. And I want to figure out the nearest location where I could draw a line.
For example if the finger tapps 30 pixels from the left side of my upper view, I want to draw a vertical line 25 pixels from the left side of the view. If the screen is tapped 40 pixels from the left, i want the line drawn 50 pixels from the left side. (So there can only be one line every 25 pixels and and I want to draw the nearest one.
Any idea how I could do this?
Drawing the line is easy:
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(100.0, 0.0, 1, 320.0)];
lineView.backgroundColor = [UIColor whiteColor];
[parentView addSubview:lineView];
But I have no idea how to find where the user tapped the screen.

To choose the nearest vertical line aligned to 25-point boundaries use this to calculate the correct x-value:
CGFloat spacing = 25.0f;
NSInteger lineNumber = (NSInteger)((touchPoint.x + (spacing / 2.0f)) / spacing);
CGFloat snapX = spacing * lineNumber;
Here's what happens in the code above:
Add half of the spacing value onto the touch point - this is because the 'snap' process in the next step will always find the previous line, so by adding half of the spacing value we ensure it will 'snap' to the nearest line.
Calculate the line number by dividing by the spacing and casting the value into an integer. This truncates the fractional part of the result so we now have the integer line number (0, 1, 2, 3, etc.).
Multiply by the original spacing to get the actual x-value of the line you want to draw (0, 25, 50, 75, etc.).

Create a transparent custom UIView that covers the whole screen (except the status bar) and override its (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event.
You can get the touch point with:
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];

You can use ssteinberg code:
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
or adding a UITapGestureRecognizer to your view, which it is probably easier:
UITapGestureRecognizer* tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[yourParentView addGestureRecognizer:tapRecognizer];
[tapRecognizer release];
...
- (IBAction)handleTap:(UITapGestureRecognizer *)recognizer {
CGPoint touchPoint = [recognizer locationInView:self.view];
...
And in addition, draw your line at every 25px by adding something like this:
CGFloat padding = 25.0;
NSInteger xPosition = round(touchPoint.x / padding) * padding;
[self drawLineAt:xPosition];

Refer This if you find it suitable :
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (lineView) {
[lineView removeFromSuperview];
NSSet *allTouchesSet = [event allTouches];
NSArray *allTouches = [NSArray arrayWithArray:[allTouchesSet allObjects]];
int count = [allTouches count];
if (count == 1) {
UITouch *touch = [[event allTouches] anyObject];
tapPoint = [touch locationInView:self];
NSLog(#"tapPoint: %#",NSStringFromCGPoint(tapPoint));
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(tapPoint.x, 0.0, 1, 320.0)];
lineView.backgroundColor = [UIColor whiteColor];
[parentView addSubview:lineView];
}
}
}

Related

iOS - How To Draw Point On A Line In Which The User Touches When Dragging

I have a rectangular view which contains vertical thin lines in which I drew programatically.
This is the code I used to draw the lines:
- (void)drawLines
{
CGFloat spacing = _bgView.frame.size.width / 11.0f;
for(int i = 1; i < 11; i++)
{
UIView *line = [[UIView alloc] initWithFrame:CGRectMake((CGFloat)(i * spacing), 0, 1, _bgView.frame.size.height)];
line.backgroundColor = [UIColor whiteColor];
line.tag = i;
[_bgView addSubview:line];
}
}
The lines are separated by equally distributed spaces calculated from the width of the rectangular view(_bgView).
Here's an overview of what it looks like:
When the user performs a drag in the rectangular view, and when his/her finger passes through or touches a line, a dot will be drawn on that specific point.
Here's the code that detects the point of touch of the user on the line, and draws a dot on that point.
- (IBAction)drawDots:(id)sender
{
UIPanGestureRecognizer *pan = (UIPanGestureRecognizer *)sender;
CGPoint pannedPoint = [pan locationInView:_bgView];
for (UIView *subview in _bgView.subviews)
{
if (CGRectContainsPoint(subview.frame, pannedPoint) && subview.tag > 0)
{
NSLog(#"Point x:%.2f y:%.2f TAG:%i", pannedPoint.x, pannedPoint.y, subview.tag);
UIView *point = [[UIView alloc] initWithFrame:CGRectMake(pannedPoint.x - 2.5, pannedPoint.y - 2.5, 5, 5)];
point.backgroundColor = [UIColor redColor];
[_bgView addSubview:point];
}
}
}
Now, if the user pans really fast, this will be the result:
As can be seen, only some points are drawn, that's why there are a lot of missing dots on the other lines.
This should be expected result:
But, this happens only when the user pans super slow.
How can I draw the points in the lines intercepted by the touch of the user even if his finger moved really fast?
Thanks!
Instead of pan gestures you should use UIResponder methods to detect user touches:
- touchesBegan:withEvent:
- touchesMoved:withEvent:
- touchesEnded:withEvent:
- touchesCancelled:withEvent:
The second method - touchesMoved:withEvent: calls when user keeps moving its finger through the screen, so you can get its finger location:
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:touch.view];
Then just add your detection logic.
Check the docs for more details:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIResponder_Class/
please refer this code
for your requirement
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
// NSLog(#"DEBUG: Touches moved" );
[super touchesMoved:touches withEvent:event];
if ([[event allTouches] count] > 1) {
} else {
UITouch *touch = [[event allTouches] anyObject];
UIView *view = touch.view;
NSLog(#"DEBUG: Touches Moved");
CGPoint location = [touch locationInView:self.bgView];
for (UIView *subview in _bgView.subviews)
{
if (CGRectContainsPoint(subview.frame, location) && subview.tag > 0) {
NSLog(#"Point x:%.2f y:%.2f TAG:%i", location.x, location.y, subview.tag);
UIView *point = [[UIView alloc] initWithFrame:CGRectMake(location.x - 2.5, location.y - 2.5, 5, 5)];
point.backgroundColor = [UIColor redColor];
[_bgView addSubview:point];
}
}
}
}
Will help you

Slide out viewcontroller from right of screen

I want to create a slide out menu from the right of my main view controller. However I want the slide out menu to appear on top of the main view and not push it to one side.
I've looked at ECSlidingViewController 2 and it allows sliding out from either side, however the slide out menus appear cut off on the exposed edges. It creates the effect of the top view controller moving left or right to reveal the hidden menu under it. I want it to appear that the hidden menu sliding out is going onto of the main view controller and that we can see its left edge so no content is cut off.
Is this at all possible and would ECSlidingViewController 2 work for this?
Edit
This is what I have tried:
To detect the touch started:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
BBAppDelegate *myAppDelegate = (BBAppDelegate*)[UIApplication sharedApplication].delegate;
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
NSArray *myViewArray = [[NSBundle mainBundle] loadNibNamed:#"FilterView" owner:self options:nil];
self.filterView = myViewArray[0];
[myAppDelegate.window addSubview:self.filterView];
if (CGRectContainsPoint(self.filterView.frame, touchLocation)) {
dragging = YES;
oldX = touchLocation.x;
oldY = touchLocation.y;
}
}
And then dragging:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if (dragging) {
CGRect frame = self.filterView.frame;
frame.origin.x = self.filterView.frame.origin.x + touchLocation.x;
frame.origin.y = self.filterView.frame.origin.y + touchLocation.y;
self.filterView.frame = frame;
}
}
However, when I start a touch, the filterView replaces all views and doesn't slide or animate in anyway. I though it was due to not providing a starting point - so I instated self.filterView like so:
self.filterView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 120, 564)];
However changing these values have no effect on the position / size of the view when its displayed.
Edit 2
Okay, I started a new project for testing and managed to get a view on screen to move around by following my touches.
I am using UIPanGestureRecognizer
Like so:
viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *myViewArray = [[NSBundle mainBundle] loadNibNamed:#"View" owner:self options:nil];
UIView *myView = myViewArray[0];
self.filterView = myView;
self.filterView.frame = CGRectMake(100, 100, 200, 200);
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self
action:#selector(dragging:)];
pan.delegate = self;
[self.filterView addGestureRecognizer:pan];
[self.view addSubview:self.filterView];
}
Then in the dragging method:
-(void)dragging:(UIPanGestureRecognizer *)gesture
{
if(gesture.state == UIGestureRecognizerStateBegan)
{
NSLog(#"Received a pan gesture");
self.panCoord = [gesture locationInView:gesture.view];
}
CGPoint newCoord = [gesture locationInView:gesture.view];
float dX = newCoord.x-self.panCoord.x;
gesture.view.frame = CGRectMake(gesture.view.frame.origin.x+dX, gesture.view.frame.origin.y, gesture.view.frame.size.width, gesture.view.frame.size.height);
}
This allows the view to move horizontally only.
Almost there - what I am trying to figure out now is getting it to snap to a location when the user lifts their finger. And how to move this view when its start position is off screen.
Edit 3
Okay, so I wanted to control the speed and velocity of the view coming out onto screen. If the user swipes quickly, the view goes into place at the speed they swipe. If they swipe slowly, it goes into place slowly. However I also set the lowest time - so if they take a long time - it defaults to the min amount.
Here is the code:
{
float start;
float end;
NSDate *methodStart;
NSDate *methodEnd;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
methodStart = [NSDate date];
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:touch.view];
start = location.x;}
-(void)dragging:(UIPanGestureRecognizer *)gesture{
if(gesture.state == UIGestureRecognizerStateBegan)
{
self.panCoord = [gesture locationInView:gesture.view];
}
CGPoint newCoord = [gesture locationInView:gesture.view];
float dX = newCoord.x-self.panCoord.x;
gesture.view.frame = CGRectMake(gesture.view.frame.origin.x+dX, gesture.view.frame.origin.y, gesture.view.frame.size.width, gesture.view.frame.size.height);
if (gesture.state == UIGestureRecognizerStateEnded){
methodEnd = [NSDate date];
NSTimeInterval executionTime = [methodEnd timeIntervalSinceDate:methodStart];
double time = executionTime;
float distance = start - end;
int distanceOverTime = distance / time;
float veloticty = distanceOverTime / 100;
double miniTimeToUse = 0.5;
double newMinTime;
if (time < miniTimeToUse){
newMinTime = miniTimeToUse;
}else {
newMinTime = time;
}
if (gesture.view.frame.origin.x+dX >= 180){
[UIView animateWithDuration:newMinTime
delay:0
usingSpringWithDamping:0.9
initialSpringVelocity:veloticty
options:0
animations:^{
self.filterView.frame = CGRectMake(300, 100, 200, 200);
} completion:nil];
}
if (gesture.view.frame.origin.x+dX <= 181){
[UIView animateWithDuration:newMinTime
delay:0
usingSpringWithDamping:0.9
initialSpringVelocity:veloticty
options:0
animations:^{
self.filterView.frame = CGRectMake(50, 100, 200, 200);
} completion:nil];
}
}
}
Now just to get it to work on pulling the view off from the side.

Define a rectangle of locations in objective-C

I have an Image in an Iphone App, I would like when a user taps a location within a defined area (range of locations or an area defined by the rectangle), it triggers another event. I know how to get a single location, But I don't know how to define a rectangular area. I am looking for a simple way to implement it. Thank you
If you want to make the rectangle visible you can add image view to your view and set up the tap recogniser. But if you don't want to make the rectangle visible you can override touchesBegan:withEvent: method and use CGRectContainsPoint:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
CGRect rect = CGRectMake(0.0, 0.0, 100, 100); //<- this is the rectangle you do check on
if (CGRectContainsPoint(rect, touchLocation)) {
NSLog(#"You tapped inside rectangle");
}
else {
NSLog(#"You missed rectangle");
}
}
Define your area as a CGRect and get the CGPoint of the touch. Then use CGRectContainsPoint to check for a hit.
If you need to create the CGRect from a list of points then you need to iterate the points and find the max and min x and y values, then you can create the CGRect with:
CGRectMake(minX, minY, maxX - minX, maxY - minY);
Maybe you can create a UIView and add a gesture recognizer like this:
UIView *customView = [UIView new];
customView.frame = CGRectMake(....); // as Wain suggest
[self.view addSubview:customView];
UITapGestureRecognizer *tapGesture =
[[UITapGestureRecognizer alloc] initWithTarget: self
action: #selector(someMethod)];
[customView addGestureRecognizer:tapGesture];

Displaying a view over an imageview which is moveable

I currently have an application where a user takes a photo or chooses from their library.
On the next controller it will show the image that was selected/taken.
What I would like to do now is show a view of some sort on top of the image view. The view would be translucent round the edges and have a circle which would show the image beneath (not transulcent). Basically this is selected a part of the image.
I then need to save some how where the view is on screen, as it should also be moveable by the user.
What is the best way to approach this?
I need an overlay view which can be moved. The circle would be a fixed size always the inside shows the imageview beneath and the outside would be a 0.5 translucency so you can still see the image but not completely. Then to save the location of the moved around circle?
---- EDIT -----
This is the example image I have created.
I have a view which has a photo as the UIImageView (bottom layer). On top of this I am trying to add a view (like the picture above). Note, the picture above is actually a png as suggested. However, this overlay is moveable. The circle is transparent (0) so you can completely see the photo below it. The outer (grey) is transparent partially (0.5) so you can still see just not completely.
The top view (circle part) would be moved around on the photo to mark a specific point on the photo. In this example if the circle is moved the side (grey) ends on screen, therefore I would need to make a huge image which takes into account the moving of the view -- which is not the best way to do this surely?
EDIT 2 ----
I now have one UIImageView over the top of the photoView (another UIImageView). The overlay is 4 times the screen with a circle in the middle of it.
When the item is moved I have a gesture recognizer that runs:
-(void)handlePan:(UIPanGestureRecognizer *)gesture {
NSLog(#"Pan Gesture");
gesture.view.center = [gesture locationInView:self.view];
}
At present from the above the UIImageView is moved from the middle point, that way the circle is what looks to be moving when the finger moves.
That is all great, but how do I implement the suggested answers into my handlePan method. So I need to check that the middle point is not too close the edge. Ideally I would like a 50 margin around the screen so the circle does not look to go completely (or mostly) off screen?
I understand from the question that you know how to do the movement of the overlay, so what I meant is just that you can simply use image views big enough to make sure that their border isn't visible.
Let's say the UIImageView would be called
UIImageView *photoImageView;
And the overlay with translucent and semi-translucent areas would be
UIImageView *imageOverlayView;
The way I see it, you need 3 different images for that imageOverlayView, depending on the device it's running on - 1 for iPad, 1 for iPhone 5 and 1 for other iPhones.
Width of that image should be equal to (screen width - circle radius) * 2, height should
CGFloat circleRadius; //this would be the radius of translucent circle
So if you set the frame of the overlay properly:
CGRect imageFrame = photoImageView.frame;
imageOverlayView.frame = CGRectMake(circleRadius - imageFrame.size.width/2, circleRadius - imageFrame.size.height/2, (imageFrame.size.width - circleRadius)*2, (imageFrame.size.height - circleRadius)*2); //origin is set to the middle of the image.
you'd never see the edge of the grey area. MZimmerman6's answer for implementation of the movement is good, but you should also make sure you block the circle from getting out of the borders of underlying image.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint new = [touch locationInView:imageOverlayView];
delX = new.x - prevPoint.x;
delY = new.y - prevPoint.y;
CGRect overFrame = imageOverlayView.frame;
overFrame.origin.x += delX;
if (overFrame.origin.x < -imageFrame.size.width) {
overFrame.origin.x = -imageFrame.size.width;
}
else if (overFrame.origin.x > 0) {
overFrame.origin.x = 0;
}
overFrame.origin.y += delY;
if (overFrame.origin.y < -imageFrame.size.height) {
overFrame.origin.y = -imageFrame.size.height;
}
else if (overFrame.origin.y > 0) {
overFrame.origin.y = 0;
}
[overlayView setFrame:overFrame];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint new = [touch locationInView:imageOverlayView];
delX = new.x - prevPoint.x;
delY = new.y - prevPoint.y;
CGRect overFrame = imageOverlayView.frame;
overFrame.origin.x += delX;
if (overFrame.origin.x < -imageFrame.size.width) {
overFrame.origin.x = -imageFrame.size.width;
}
else if (overFrame.origin.x > 0) {
overFrame.origin.x = 0;
}
overFrame.origin.y += delY;
if (overFrame.origin.y < -imageFrame.size.height) {
overFrame.origin.y = -imageFrame.size.height;
}
else if (overFrame.origin.y > 0) {
overFrame.origin.y = 0;
}
[overlayView setFrame:overFrame];
}
EDIT
With your pan gesture recognizer, checking if you aren't going too far needs a little change to the handlePan: method.
-(void)handlePan:(UIPanGestureRecognizer *)gesture {
CGPoint newCenter = [gesture locationInView:self.view];
if (newCenter.x < 50) {
newCenter.x = 50;
}
else if (newCenter.x > self.view.frame.size.width - 50) {
newCenter.x = self.view.frame.size.width - 50;
}
if (newCenter.y < 50) {
newCenter.y = 50;
}
else if (newCenter.y > self.view.frame.size.height - 50) {
newCenter.y = self.view.frame.size.height - 50;
}
gesture.view.center = newCenter;
}
If that 50 points margin is equal to circle radius, this will make sure your circle able to touch the edges of the screen, but unable to go beyond them.
I would first initialize some UIImageView with a png image in it that will easily handle this moving frame with a hole in the center. You can the add this to your screen. Once this is done, use the touchesBegan, touchesMoved and other touch commands to find whether a user is touching that box and from that determine how far the user has moved from the previous point. Use this difference to then add or subtract values from the images current frame origin or center, and voila you have a moving box.
EDIT
In View.h
CGPoint prevPoint;
float delX;
float delY;
UIView *overlayView;
In View.m
-(void) viewDidLoad {
overlayView = [UIView alloc] initWithFrame:CGRectMake(100,100,100,100)];
[overlayView setBackgroundColor:[UIColor clearColor]];
[self.view addSubview:overlayView];
UIImageView *circImage = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,overlayView.frame.width,overlayView.frame.height)];
[circImage setImage:[UIImage imageNamed:#"SomeImage.png"]];
[overlayView addSubview:circImage];
[self.view setNeedsDisplay];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
prevPoint = [touch locationInView:overlayView];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint new = [touch locationInView:overlayView];
delX = new.x - prevPoint.x;
delY = new.y - prevPoint.y;
CGRect overFrame = overlayView.frame;
overFrame.x += delX;
overFrame.y += delY;
[overlayView setFrame:overFrame];
[self.view setNeedsDisplay];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint new = [touch locationInView:overlayView];
delX = new.x - prevPoint.x;
delY = new.y - prevPoint.y;
CGRect overFrame = overlayView.frame;
overFrame.x += delX;
overFrame.y += delY;
[overlayView setFrame:overFrame];
[self.view setNeedsDisplay];
}

Why a subView won't stay in the parent frame?

I have a view and inside it there's a UIImage. The image isn't static and I can move it around if I drag my finger around (using drag events) The problem is that sometimes the picture moves outside of the UIView frame. What's the appropriate way to keep it inside the parent frame bounds?
--UIViewA
--------UIViewB
--------------UIImage
--------------UIButton
I want to keep UIImage inside UIViewB
- (IBAction)myButtonSingleTap:(UIButton *)sender {
imDragging = YES;
[_myButton addTarget:self action:#selector(dragBegan:withEvent:) forControlEvents: UIControlEventTouchDown];
}
- (IBAction)myButtonDragInside:(UIButton *)sender
{
[_myButton addTarget:self action:#selector(draging:withEvent:) forControlEvents: UIControlEventTouchDragInside];
}
- (void)dragBegan:(UIControl *)c withEvent:ev {
UITouch *touch = [[ev allTouches] anyObject];
startingTouchPoint = [touch locationInView:self.view];
}
- (void)draging:(UIControl *)c withEvent:ev {
UITouch *touch = [[ev allTouches] anyObject];
currentTouchPoint = [touch locationInView:self.view];
_movingPic.frame = CGRectMake(currentTouchPoint.x, currentTouchPoint.y, 28, 23);
}
You will need to check the location of the view during the dragging.
At some point you will be setting the frame of the image depending on the user's drag direction, etc...
During this you should have a logic check like...
If new location x value is less than 0 then set new location x = 0.
If new location x value plus image width is greater than view width then set new location x = view width - image width.
etc...
Then use the new location as the point to move the image to.
Try adding the touch recogniser to the parent view and not the entire view
Before setting the new frame, make sure it is contained by the moving view's superview's bounds.
- (void)draging:(UIControl *)c withEvent:ev
{
UITouch *touch = [[ev allTouches] anyObject];
currentTouchPoint = [touch locationInView:self.view];
CGRect newFrame = CGRectMake(currentTouchPoint.x, currentTouchPoint.y, 28, 23);
newFrame.x = MAX(newFrame.x, 0);
newFrame.y = MAX(newFrame.y, 0);
newFrame.x = MIN(newFrame.x, _movingPic.superview.bounds.size.width - 28);
newFrame.y = MIN(newFrame.y, _movingPic.superview.bounds.size.height - 23);
_movingPic.frame = newFrame;
}

Resources