Displaying a view over an imageview which is moveable - ios

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];
}

Related

Change UIBezierArc colour, when its handle is drawn inside or outside from handle only

I have a circular slider, which has a bezier arc drawn in it, an arc has two handles at start and end point in slider, arc is drawn in circular slider.
I am able to draw bezier curve along the circular slider with the help of start and end handles.
I want to change the colour of arc when it is dragged 45 inside or 45 outside from handle only and it should not change arc colour when it is dragged in the circular slider.
Note- while dragging inside and outside it should not change other slider arc's color.
The color should only change, when it is dragged inside and outside only not when it is dragged in circle.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
_dragging = YES;
if (!self.isDragging)
{
return;
}
if ([self.delegate respondsToSelector:#selector(sliderDraggin:)])
{
[self.delegate sliderDraggin:self];
}
UITouch *touch = [touches anyObject];
//UITouch *touch = [touches anyObject];
NSUInteger nearestHandleIndex = [self indexOfNearesPointForTouch:touch];
_nearesHandle = (nearestHandleIndex != NSNotFound) ? _handles[#(nearestHandleIndex)] : nil;
CGPoint location = [touch locationInView:self];
CGFloat distance = [_math distanceBetweenPoint:_centerPoint andPoint:location];
CGFloat inOff = _radius - _sliderWidth - _offset.inside;
CGFloat outOff = _radius + _offset.outside;
if (distance < inOff || distance > outOff)
{
if (self.isNeedToRevoke)
{
_dragging = NO;
return;
}
}
dispatch_async(dispatch_get_main_queue(), ^{
int a = AngleFromNorth(_centerPoint, location, EVA_FLIPPED);
[self moveView:_nearesHandle toAngle:a];
[self drawArc];
});
[self informDelegateAboutHandles];
}
You've calculated the distance.
So, first, don't exit if it's less than < inOff or > outOff.
Instead, set the color based upon distance, perhaps:
UIColor *arcColor;
if (distance < (inOff - 45)) {
arcColor = [UIColor redColor];
} else if (distance > (outOff + 45)) {
arcColor = [UIColor redColor];
} else {
arcColor = [UIColor greenColor];
}
Clearly, tweak the logic as you see fit, but that's likely to be the basic idea.
Use this arcColor to update whatever color-specific property is used by your drawing routine.
I don't have enough to figure out how you rendered these arcs, but this repo contains sample app that updates strokeColor of CAShapeLayer for the individual arcs based upon the touches, yielding the following:
I wouldn't worry too much about reconciling my implementation with yours, but it just illustrates the pattern shown above, where you calculate which arc to update in touchesBegan and update the color in touchesMoved.

Different coordinates while resizing an imageview iOS

I'm trying to resizing an imageview using these functions:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* mTouch = [touches anyObject];
if (mTouch.view == [self Logo_01]) {
CGPoint cp = [mTouch locationInView:[self view]];
[[mTouch view]setCenter:CGPointMake(cp.x-xd, cp.y-yd)];
NSLog(#"questo รจ x");
NSLog(#"lalal %f", cp.y);
if (cp.y > 390) {
[_Logo_01 setHidden:YES];
}
if (cp.y < 130) {
[_Logo_01 setHidden:YES];
}
if (cp.x > 290) {
[_Logo_01 setHidden:YES];
}
if (cp.x < 40) {
[_Logo_01 setHidden:YES];
}
}
when i resize my Logo on the app the image resizes correctly but my center point is wrong
I'm sorry i wronged to post code
this is the code that i use to scale my images:
- (IBAction)ScaleImage1:(UIPinchGestureRecognizer *)recognizer
{
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
recognizer.scale = 1;
CGFloat height = _Logo_01.frame.size.height;
NSLog(#"SIZE ===== %f", height);
if (height > 600) {
[_Logo_01 setHidden:YES];
}
}
try to write same style everywhere (brackets written randomly, spaces, newlines) - it's easier to read and shows that the dev is better.
Logo_01 is just horrible name for UI element. It's good for asset name. Refactor and rename it to eg startingLogoImageView.
Comparing views with == and isEqual works in a different way. One compares pointers, other one objects. Please read about it, as that's a serious bug if that's unintended.
What is xd in setCenter: line? Where is it from?
We try not to use shortcuts in Objective-C. You should name cp touchPoint as it's more obvious what it is in fact. That's the way recommended by Apple and used by devs.
So the code:
// extern NSUInteger const kViewOffsetY; // uncomment this line if you want it accessible via other classes, delete otherwise
NSUInteger const kViewOffsetY = 1;
NSUInteger const kViewOffsetX = 1;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([touch.view isEqual:startingLogoImageView]) {
UIView *touchedView = touch.view;
CGPoint touchPoint = [touch locationInView:self.view];
[touchedView setCenter:CGPointMake(CGRectGetMidX(touchedView.frame) - kViewOffsetX, CGRectGetMidY(touchedView.frame) - kViewOffsetY)];
// where are these values from? you probably should calculate it more dynamically
BOOL shouldHideView = touchPoint.y > 390 || touchPoint.y < 130 || touchPoint.x > 290 || touchPoint.x < 40;
if (shouldHideView) {
[touchedView setHidden:YES];
}
}
}
Why was it working wrong? Because in setCenter: method you were using top left corner (x and y values of view are in top left corner not in the center), while you should use the center point.

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;
}

UITextView text space go smaller during rezise

I'd like to enable the user to resize an UITextView. I display a button in the bottom rigth corner of the UITextView such as when the user moves it, the UITextView follow it and is resized.
This is works fine but the text space has a strange behaviour. The text space seems to get smaller and smaller during the resize. So if I resize a lot, go to a big frame and then to a small frame, the text space has become smaller than the frame.
- (id) init{//My subclass of UITextView
self = [super init];
if(self){
//Resize button
//Init ResizeButton
self.resizeButton = [[UIButton alloc]init];
//Image
[self.resizeButton setImageForAllState:[UIImage imageNamed:#"Isosceles-right-triangle.png"]];
//Target
[self.resizeButton addTarget:self action:#selector(wasDragged:withEvent:) forControlEvents:UIControlEventTouchDragInside];
[self.resizeButton addTarget:self action:#selector(wasDragged:withEvent:) forControlEvents:UIControlEventTouchDragOutside];
//Add to view
[self addSubview:resizeButton];
//Self
//text font
[self setFont:[UIFont fontWithName:#"papyrus" size:17]];
}
return self;
}
- (void)wasDragged:(UIButton *)button withEvent:(UIEvent *)event
{
// NSLog(#"wasDragged");// get the touch
UITouch *touch = [[event touchesForView:button] anyObject];
// get delta
CGPoint previousLocation = [touch previousLocationInView:button];
CGPoint location = [touch locationInView:button];
CGFloat delta_x = location.x - previousLocation.x;
CGFloat delta_y = location.y - previousLocation.y;
// move button
button.center = CGPointMake(button.center.x + delta_x,
button.center.y + delta_y);
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width+ delta_x, self.frame.size.height+ delta_y);
}
The view is also draggable
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
// Calculate offset
CGPoint pt = [[touches anyObject] locationInView:self];
float dx = pt.x - startLocation.x;
float dy = pt.y - startLocation.y;
CGPoint newcenter = CGPointMake(self.center.x + dx, self.center.y + dy);
// Set new location
self.center = newcenter;
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// Calculate and store offset, and pop view into front if needed
CGPoint pt = [[touches anyObject] locationInView:self];
startLocation = pt;
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
}
EDIT
Before resizing
Resizing
Back to original size
Well, I have this problem too. The soliution is to subclass UITextView and override setFrame: method. Before setting your wanted frame, set CGRectZero frame for textView. It will fix your problem. Code bellow.
- (void)setFrame:(CGRect)frame
{
[super setFrame:CGRectZero];
[super setFrame:frame];
}
Seems like you could do one of two things:
First: turn off the 'Adjust to fit' of the control
textfield.adjustsFontSizeToFitWidth = NO;
Second: Hardcode a font size into the code
textfield.font = [UIFont fontWithName:#"desired font name" size:10];

Boundary for draggable image ios

I'm trying to create a draggable image, but i'm trying to confine it's dragging to within a small square rather than the full screen. Could someone tell me where i'm going wrong?. I've placed the code that I have so far below:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
if([touch view] == dot) {
CGPoint location = [touch locationInView:self.view];
dot.center = location;
if (location.x >10) {
location.x =10;
} else if (location.x <10) {
location.x = 10;
}
if (location.y >20) {
location.y =20;
} else if (location.y < 20) {
location.y = 20;
}
}
}
You are assigning location before you are making changes to it.
Apply your limits to location first then assign it to dot.
Also, your limits you are showing would lock your position to 10,20 since you are not allowing it to be more than 10 or less than 10. Likewise with 20.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
if([touch view] == dot) {
CGPoint location = [touch locationInView:self.view];
location.x = MIN(MAX(location.x, 0),10);
location.y = MIN(MAX(location.y, 0),20);
dot.center = location;
}
}
I have implemented an image dragging function recently like this. I use the PAN Gesture to move the image which results in two CGFloats "endPointX and endPointY". In the code below between the comments "Stay on Screen Check" and "End Stay on Screen check", I check if these are on the screen. If not I adjust them to prevent the image moving off the screen.
I hope that helps. If you want to move the image within a small part of the total screen then I would add the image to a holder subview and then check the holder view .bounds.size.width/height above instead.
CGFloat endPointX = translatedPoint.x + (.35*[(UIPanGestureRecognizer*)sender
velocityInView:self.view].x);
CGFloat endPointY = translatedPoint.y + (.35*[(UIPanGestureRecognizer*)sender velocityInView:self.view].y);
// Stay on the screen check
if(endPointX < 0) {
endPointX = 0;
} else if(endPointX > self.view.bounds.size.width) {
endPointX = self.view.bounds.size.width;
}
if(endPointY < 0) {
endPointY = 0;
} else if(endPointY > self.view.bounds.size.height) {
endPointY = self.view.bounds.size.height;
}
// End of the Stay on Screen check
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:.35];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[[sender view] setCenter:CGPointMake(endPointX, endPointY)];
[UIView commitAnimations];

Resources