Check for overlapping views after transform - objective-c

I have multiple UIImageView's within a UIView. These UIImageViews have been transformed.
When the gesture is applied to any one of the UIImageView's the overlapping imageViews should get displaced in such a way that the move outside the UIImageView's frame and we get a full view of the image (all of this would be done in an animating manner).
Also please note that the images will have been transformed so I wont be able to use the frame properties.
Right now I'm using CGRectIntersectsRect but its not giving me the desired effect.

After struggling a lot, finally i achieved the desired result with this algorithm.
I calculate the list of overlapping UIImageView's and store it in an array.
I am using CGRectIntersectsRect for this.
Note : This will also include the UIImageView on which the gesture was applied (say target ImageView).
For each of these overlapping UIImageView, i perform the following steps :
1) Calculate the angle between the overlapping ImageView and target
ImageView. This will give me the direction to shift the Overlapping ImageView.
2) Now shift the overlapping ImageView by some constant distance(say len), such that it maintains the same angle. Direction should be such that it moves away from the target ImageView.
3) You can calculate the new x,y co-ordinates using Sin/Cos functions.
x = start_x + len * cos(angle);
y = start_y + len * sin(angle);
NOTE: For ImageViews whose center is less than your target ImageView's center, you will need to subtract the value.
4) Now shift the overlapping ImageView to the new calculated center.
5) Continue shifting it until the views do not intersect any more.
I am attaching my code. I hope it helps.
-(void)moveImage:(UIImageView *)viewToMove fromView:(UIImageView
*)imageToSendBack
{
CGFloat angle = angleBetweenLines(viewToMove.center, imageToSendBack.center);
CGFloat extraSpace = 50;
CGRect oldBounds = viewToMove.bounds;
CGPoint oldCenter = viewToMove.center;
CGPoint shiftedCenter;
while(CGRectIntersectsRect(viewToMove.frame,imageToSendBack.frame))
{
CGPoint startPoint = viewToMove.center;
shiftedCenter.x = startPoint.x - (extraSpace * cos(angle));
if(imageToSendBack.center.y < viewToMove.center.y)
shiftedCenter.y = startPoint.y + extraSpace * sin(angle);
else
shiftedCenter.y = startPoint.y - extraSpace * sin(angle);
viewToMove.center = shiftedCenter;
}
viewToMove.bounds = oldBounds;
viewToMove.center = oldCenter;
[self moveImageAnimationBegin:viewToMove toNewCenter:shiftedCenter];
}

I didn't try this, but I would do it like this:
After getting the bool from CGRectIntersectsRect, multiply this overlapping UIImageView x and y by the width and height of it.
And since you can't use frame property after changing the transformation, you will need to multiply the width and height by 1.5 for the maximum amount of rotation.
Warning: If the transform property is not the identity transform, the
value of this property is undefined and therefore should be ignored.
This might move some view father than other, but you will be certain all views will be moved outside the UIImageView's frame.
If you try it and it doesn't work, post some code and I will help you with it.
EDIT: For views that their x,y less than your UIImageView's x,y you will need to subtract the offset.

Related

Dynamically changing size and frame position of UITextView

What I would like to do is to make a custom UITextView to allow dynamically changing size and positions of it like PicLab's text edit function.
PicLab tools article
In sum for my simple questions following
1, How to move a position of TextView at your disposal
2, How to change a size of TextView by finger slide
3, How to dynamically change size of fonts corresponding to size of TextView
That would be great if sharing me with useful links as well as codes.
to move the position of a UITextView (actually of any UI element in iOS), you can modify its frame property. the frame property defines the rectangle that this elements takes on the screen, being defined with x and y positions ( (0, 0) is the top left corner of the screen).
you can also change the size of a UITextView by modifying the frame, this time instead of modifying x and y, you'll have to modify its height and width
an example of setting a UITextField with a width of 200 and height of 50 would look like in the top left corner of the screen looks like:
CGFloat x = 0.0;
CGFloat y = 0.0;
CGFloat width = 2000.0;
CGFloat height = 50.0;
textField.frame = CGRectMake(x, y, width, height)
the changing of the font size will have to be implemented dynamically by a custom function of yours, you can use the class method of UIFont fontWithName:size for this.
Frame cannot be set for coordinates and dimension directly, you have to assign a new object. A quick search found this, Change ios Frame Size(width e height) keeping position (x,y).

How to get the padding from the edge of the UITableview to the UITableViewCell

On the iPad, the Grouped style tableview's cells are inset deeper from the edge of the tableview than on the iPhone.
I need to retrieve the Left and Right distances from the edges of the tableview to where the cell begins. What i'm referring to is similar to "Margins". I read the UITableview API up and down and can't find a property that returns this.
I need to use this in calculation to compute where to position content in my cells.
Thanks in advance!
Alex
I haven't tested this but i'm pretty sure you should just be able to pick up the frame of both and then compare from there.
CGRect cellFrame = yourCell.frame;
CGRect tableFrame = yourUITableView.frame;
The CGRect values are (x coordinate, y coordinate, width, height).
Also you can just print out the frames using :
NSLog(#"your cell frame is %#",NSStringFromCGRect(yourCell.frame);
NSLog(#"your table frame is %#",NSStringFromCGRect(yourUITableView.frame);
I solved this with overriding the layoutSubviews call for the iPad and setting the grouped view margins to what I want them to be, rather then what the apparently hidden value is. Other answers in Stack point out that it can vary from 10 to 45 pixels in width.
In your CustomTableViewCell class
- (void)layoutSubviews
{
CGRect f = self.bounds;
f = CGRectInset(f, 10, 0);
self.contentView.frame = f;
self.backgroundView.frame = f;
}
You could force it to keep contentView and backgroundView to be equal to that of the TableCell width which is that TableView width, but in this case I still wanted my grouped view to be inset a little bit. It also allows you to better match with a custom header/footer view which will go edge to edge without work.

iOS - Math help - base image zooms with pinch gesture need overlaid images adjust X/Y coords relative

I have an iPad application that has a base image UIImageView (in this case a large building or site plan or diagram) and then multiple 'pins' can be added on top of the plan (visually similar to Google Maps). These pins are also UIImageViews and are added to the main view on tap gestures. The base image is also added to the main view on viewDidLoad.
I have the base image working with the pinch gesture for zooming but obviously when you zoom the base image all the pins stay in the same x and y coordinates of the main view and loose there relative positioning on the base image (whose x,y and width,height coordinates have changed).
So far i have this...
- (IBAction)planZoom:(UIPinchGestureRecognizer *) recognizer;
{
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
recognizer.scale = 1;
for (ZonePin *pin in planContainer.subviews) {
if ([pin isKindOfClass:[ZonePin class]]){
CGRect pinFrame = pin.frame;
// ****************************************
// code to reposition the pins goes here...
// ****************************************
pin.frame = pinFrame;
}
}
}
I need help to calculate the math to reposition the pins x/y coordinates to retain there relative position on the zoomed in or out plan/diagram. The pins obviously do not want to be scaled/zoomed at all in terms of their width or height - they just need new x and y coordinates that are relative to there initial positions on the plan.
I have tried to work out the math myself but have struggled to work it through and unfortunately am not yet acquainted with the SDK enough to know if there is provision available built in to help or not.
Help with this math related problem would be really appreciated! :)
Many thanks,
Michael.
InNeedOfMathTuition.com
First, you might try embedding your UIImageView in a UIScrollView so zooming is largely accomplished for you. You can then set the max and min scale easily, and you can scroll around the zoomed image as desired (especially if your pins are subviews of the UIImageView or something else inside the UIScrollView).
As for scaling the locations of the pins, I think it would work to store the original x and y coordinates of each pin (i.e. when the view first loads, when they are first positioned, at scale 1.0). Then when the view is zoomed, set x = (originalX * zoomScale) and y = (originalY * zoomScale).
I had the same problem in an iOS app a couple of years ago, and if I recall correctly, that's how I accomplished it.
EDIT: Below is more detail about how I accomplished this (I'm looking my old code now).
I had a UIScrollView as a subview of my main view, and my UIImageView as a subview of that. My buttons were added to the scroll view, and I kept their original locations (at zoom 1.0) stored for reference.
In -(void)scrollViewDidScroll:(UIScrollView *)scrollView method:
for (id element in myButtons)
{
UIButton *theButton = (UIButton *)element;
CGPoint originalPoint = //get original location however you want
[theButton setFrame:CGRectMake(
(originalPoint.x - theButton.frame.size.width / 2) * scrollView.zoomScale,
(originalPoint.y - theButton.frame.size.height / 2) * scrollView.zoomScale,
theButton.frame.size.width, theButton.frame.size.height)];
}
For the -(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView method, I returned my UIImageView. My buttons scaled in size, but I didn't include that in the code above. If you're finding that the pins are scaling in size automatically, you might have to store their original sizes as well as original coordinates and use that in the setFrame call.
UPDATE...
Thanks to 'Mr. Jefferson' help in his answer above, albeit with a differing implementation, I was able to work this one through as follows...
I have a scrollView which has a plan/diagram image as a subview. The scrollView is setup for zooming/panning etc, this includes adding UIScrollViewDelegate to the ViewController.
On user double tapping on the plan/diagram a pin image is added as a subview to the scrollView at the touch point. The pin image is a custom 'ZonePin' class which inherits from UIImageView and has a couple of additional properties including 'baseX' and 'baseY'.
The code for adding the pins...
- (IBAction)planDoubleTap:(UITapGestureRecognizer *) recognizer;
{
UIImage *image = [UIImage imageNamed:#"Pin.png"];
ZonePin *newPin = [[ZonePin alloc] initWithImage:image];
CGPoint touchPoint = [recognizer locationInView:planContainer];
CGFloat placementX = touchPoint.x - (image.size.width / 2);
CGFloat placementY = touchPoint.y - image.size.height;
newPin.frame = CGRectMake(placementX, placementY, image.size.width, image.size.height);
newPin.zoneRef = [NSString stringWithFormat:#"%#%d", #"BF", pinSeq++];
newPin.baseX = placementX;
newPin.baseY = placementY;
[planContainer addSubview:newPin];
}
I then have two functions for handling the scrollView interaction and this handles the scaling/repositioning of the pins relative to the plan image. These methods are as follows...
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return planImage;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
for (ZonePin *pin in planContainer.subviews) {
if ([pin isKindOfClass:[ZonePin class]]){
CGFloat newX, newY;
newX = (pin.baseX * scrollView.zoomScale) + (((pin.frame.size.width * scrollView.zoomScale) - pin.frame.size.width) / 2);
newY = (pin.baseY * scrollView.zoomScale) + ((pin.frame.size.height * scrollView.zoomScale) - pin.frame.size.height);
CGRect pinFrame = pin.frame;
pinFrame.origin.x = newX;
pinFrame.origin.y = newY;
pin.frame = pinFrame;
}
}
}
For reference, the calculations for position the pins, by the nature of them being pins' centres the pin image on the x axis but has the y-axis bottom aligned.
The only thing left for me to do with this is to reverse the calculations used in the scrollViewDidScroll method when I add pins when zoomed in. The code for adding pins above will only work properly when the scrollView.zoomScale is 1.0.
Other than that, it now works great! :)

iOS: Dynamically drawing on canvas(context)

I'm currently making an application which draws out a certain network of signs to the screen (on a CGContextRef). So far everything is going great, but now i'm finding myself in the situation that i can't solve this problem:
I'm trying to draw an object dynamically knowing only the line its on (have the start and ending point's x and y coordinates). With these i found the middle of the line, this is where the symbol should be drawn. With this information i found the angle of the line (with the top as 0). This is the information i have right now:
CGPoint firstLocation;
CGPoint secondLocation;
CGPoint middleLocation;
double x1 = firstLocation.x;
double y1 = firstLocation.y;
double x2 = middleLocation.x;
double y2 = middleLocation.y;
float a = (atan2(y2-y1, x2-x1) * (180/M_PI)) - 90;
I looked at using some transform function (like CGAffineTransform) on a CGRect, but this doesn't seem to work as i need to rotate the rect around it's center and a CGRect would only rotate around it's origin.
I want to create the following symbols with the above information:
Any help is appreciated, and if you need any more information please tell me!
In my app I do something similar. I have a path that I add a transform to before drawing. The transform shifts the path to the midpoint, rotates it, and shifts it back:
// Rotate the path such that it points to the end coordinate
CGAffineTransform t = CGAffineTransformTranslate(
CGAffineTransformRotate(
CGAffineTransformMakeTranslation(middleLocation.x, middleLocation.y),
-a),
-middleLocation.x, -middleLocation.y);
CGMutablePathRef path = CGPathCreateMutable();
CGPoint points[8] = { ... these are the 8 points in my path ... };
CGPathAddLines(path, &t, points, 8);
You don't have to use CGPathAddLines, that was just the easiest way for me to construct the path. All of the CGPathAdd... functions can take a transform.
If you're not using CGPath, you could do a similar transform in the context itself by doing CGContextTranslateCTM and CGContextRotateCTM.

Maintain scroll position in UIWebView when resizing

I have a UIWebView which resizes when the device is rotated. When the webview is resized the scroll position from the top of the page remains the same, but since the height of the content is changing, you end up in a different place at the end of the rotation.
The content of the webview is text and images which are not being scaled to fit. At a smaller width the text breaks more making it taller.
Is there a good way to maintain the scroll position?
One way to kind of work around this problem is to adjust the contentOffset of the webviews's scrollview after a rotation. Due to changes in line breaks in texts the overall size of the page may change during a rotation.
So to solve this, you have to save the old size of the webviews content in the "willRotateToInterfaceOrientation"-method, then calculate the relative change in the "didRotateFromInterfaceOrientation"-method and adjust the scrollviews contentOffset. (That 'almost' solve the problem - i say almost because due to the changes in line breaks, you might end up one or two lines off your desired position.)
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
oldSize = self.webView.scrollView.contentSize;
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
CGSize newSize = self.webView.scrollView.contentSize;
float xFactor = newSize.width / oldSize.width;
float yFactor = newSize.height / oldSize.height;
CGPoint newOffset = webView.scrollView.contentOffset;
newOffset.x *= xFactor;
newOffset.y *= yFactor;
webView.scrollView.contentOffset = newOffset;
}
Take a look at my solution: https://github.com/cxa/WebViewContentPositioner. In short, I use document.caretRangeFromPoint(0, 0) to capture the Range, and insert an element to track its position. After resizing, use the captured Range info to decide position.