I have a circle (UIImage) divided into four sections (triangles).
Also I know tree points of the sections, center, upper left and upper right.
How can i calculate which sections is touched with UITapGestureRecognizer?
If you have a CGPathRef of each of your shapes, you can call CGPathContainsPoint with the value returned by your gesture recognizer’s -locationInView: on each of the paths until you find the one you tapped.
Alternatively, you can split the image into four separate images and set them as the images of four separate buttons with their own target/action handlers.
Related
I want to call the action (go to another view) when user tap specific area of image (black dots): . Image fills whole view, content mode is 'Aspect Fit'. The problem is that when I setup it on one screen size (e.g. iPhone 8) on another the 'tap area' is shifted. I've tried to solve this with button and constraints or UITapGestureRecognizer with point conversion using screen resolution (nativeBounds), but nothing helps.
It is possible to use constraints to match the positions of the circles with UIButtons. The trick is to use the multiplier of the constraint to scale the buttons width/height and position to the screen size.
I'll describe how to do it for one button, and then you can repeat it for the others. I assume the image is 657 wide by 918 high. If I have the dimensions reversed, you'll need to substitute the actual values for the ones I have used.
Create a UIView to hold the image and buttons. Give this view an aspect ratio constraint with multiplier 657:918 which is width:height. Add the UIImageView to this view and constrain its 4 edges to the edges of the view with 0 offsets. Give this view constraints to the left and right edges of the main view and give it a vertical constraint to place it on the screen.
Get the width/height of the circle in the image and the horizontal and vertical positions of the right edge and bottom edge. For example, the topmost circle is 106 x 106 and ends at horizontal position 392 and the bottom is at 338.
Set the width of the button equal to the width of the containing view with multiplier 106:657 which is width of circle:width of the image.
Set the height of the button equal to the height of the containing view with multiplier 106:918 which is height of circle:height of the image.
Set the trailing edge of the button equal to the trailing edge of the containing view with multiplier 392:657 which is end of circle:width of image.
Set the bottom edge of the button equal to the bottom edge of the containing view with multiplier 338:918 which is bottom of circle:height of the image.
This will allow the button to stay aligned with the circle on all devices. Repeat steps 2 through 6 for the other circles.
Instead of using an image, you can try creating your own UIView subclass called BlackDotsView.
In the draw(_rect:) method, you can draw the lines. To determine where the lines start and end, you need to do some maths with the view's width and height. You calculate where all the lines end and create UIBezierPaths and then you stroke the paths.
In the initializer of BlackDotsView, you can add the dots as subviews. To make them circular, just set dotView.layer.cornerRadius to half the dot's width. Then, you can add UITapGestureRecognizers to the dot views.
You can follow the delegate pattern by creating a BlackDotsViewDelegate that has a method called dotTapped(index:). When a dot is tapped, you would call the delegate method and pass the index of the dot.
how can i add an X looking red stroke to a UIImageView.
i would like to add 2 diagonal red lines to a UIImageView, is there a way to do it programmatically using layers or masks? (not in drawRect)
Use a CAShapeLayer with your X shape as its path. Depending on how you've drawn the path, you may want to set a nil fill colour (since a path just made of two crossed lines should not be filled).
Add the shape layer as a sublayer of your image view.
I have two UIViews, both of which obviously have CGRects each. One UIView fills the screen whereas the other just fills part of it in the middle. I have the CGRect of the smaller UIView, but how can I get the CGRect of the one outside of it - excluding the size of the smaller one so that the background can be dimmed, but not the content of the inner UIView?
This is what my UIViewController looks like so you can get a better idea of what I'm trying to do:
I want to dim the outer UIView, not the inner one - but I don't have the CGRect of the outer one excluding the inner UIView so I've had to do it the other way around for now.
A CGRect is a square size. If you want to know the outer aria you need to compute 4 CGRects. The top, left, right and bottom space.
So, it looks to me like the outer view is responsible for drawing your content and the inner view is your selection rectangle right?
If so, then I think you are going to want to draw your content twice when you have this selection mode active. You will:
draw one pass dimmed - adjust the colors yourself (you can either draw the whole area or clip away the inner area using code from here)
draw the second inner pass with a clip rect setup so only your content inside the rect for your selection rectangle is drawn
Or
draw all your content like normal
set your clipping mask (using code from here so that just the outer area is rendered) and then draw a black rect with some degree of transparency over your whole scene
I am looking to display number line in my application
How this can be done? I also want to control number like programatically
you can use views with different sizes and background colours. Use #define -s for the fixed sizes.
You should have 3 functions:
one that draws the horizontal red/blue rects
-(UIView *) drawHorizontal:(CGRect)frame withColor:(UIColor)theColor;
one that draws vertical spacers and labels underneath. Iterate for whole line width.
-(UIView *) drawVertical:(CGPoint)centerPosition withLabel:(int)number;
Your number line should be stored as an NSArray of NSDictionaries containing the position (index) and length of the horizontal red/blue lines.
Hope this helps.
There's no easy standard component to do this. You will need to subclass UIView and override drawRect. Then you can use Core Graphics to draw the number line.
I am looking for an efficient way to handle the image/frame detection from touch methods. Let's say i am building a keyboard or similar to this. I have 'n' number of images placed on the UI. When someone touches an alphabet (which is an image), i can do the following to detect the corresponding letter
1) CGRectIntersectsRect(..,..) : if i use this, then i need to check each & every letter to find out what letter exists at that touch point (let's say 100,100). This becomes O(n). If i move my finger accross the screen, then i will get m points & all corresponding image detection becomes O(n*m) which is not good.
2) Other way is building a hash for each & every x,y position so that the look up will be simply O(1). But again this will be a memory constraint as i need to store 300*300 ( assuming i am using 300*300 screen size). if i reshuffle my letters, then everything needs to calculated again. So this is not good
In other words, i need some thing like , given a point (x,y), i need some way of finding which rectangle is covering that point efficiently.
Sorry for long post & any help would be grateful.
Thanks
If there are in a regular grid, then integer division by the grid size. Assuming you've a small, fixed screen size, a bucket array gives as similar gain ( a 2D grid, where each entry is a list of the rectangles which intersect that part of the grid ) is very fast if tuned correctly so the lists only have a few members. For unbounded or large spaces, KD trees can be used.
It's useful to have the rects you want as final targets set up as subviews as a larger UIView (or subclass) where you expect all these related hits to occur. For example, if you're building your own keyboard, you could add a bunch of UIButton objects as subviews and hit test those.
So, the easy and traditional way of hit testing a bunch of subviews is to simply have code triggered by someone hitting those buttons. For example, you could add the subviews as UIControl objects (which is a subclass of UIView that adds some useful methods for catching user touch events), and call addTarget:action:forControlEvents: to specify some method to be triggered when the user does something in the rect of that UIControl. For example, you can catch things like UIControlEventTouchDown or UIControlEventTouchDragEnter. You can read the complete list in the UIControl class reference.
Now, it sounds like you might be going for something even more customized. If you really want to start with a random (x,y) coordinate and know which rect it's in, you can also use the hitTest:withEvent: method of UIView. This method takes a point in a view, and finds the most detailed (lowest in the hierarchy) subview which contains that point.
If you want to use these subviews purely for hit testing and not for displaying, then you can set their background color to [UIColor clearColor], but don't hide them (i.e. set the hidden property to YES), disable user interaction with them (via the userInteractionEnabled BOOL property), or set the alpha below 0.1, since any of those things will cause the hitTest:withEvent: method to skip over that subview. But you can still use an invisible subview with this method call, as long as it meets these criteria.
Look at UIView's tag property. If your images are in UIViews or subviews of UIView, then you can set each tag and use the tag to look up in an array.
If not, then you can improve the speed by dividing your array of rectangles into sets that fit into larger rectangles. Test the outer rectangles first, then the inner rectangles. 25 rectangles would need only 10 tests worst case as 5 sets of 5.
Thanks Pete Kirkham & Tyler for your answers which are really helpful. Lets say i dont wanna use buttons as i am mainly displaying images as a small rectangles. To check for the rect # (x,y) , i can trigger that easily by making my grid as a square & finding
gridcolumn = Math.floor(pos.x / cellwidth); gridrow = Math.floor(pos.y / cellheight);
But my problem is with touchesMoved. Lets say i started # grid-1 & dragged till grid-9 (in a 3*3 matrix), in this case, i am assuming i will get 100-300 (x,y) positions, so everytime i need to run above formula to determine corresponding grid. This results in 300 calculations which might effect the performance.
So when i display an image as a rect, can i associate some id for that image? so that i can simply just save the ids in a list (from grid-1 to grid-9) so that i can avoid the above calculation.
Thanks for your help