iOS: convertRect:toView: doesn't work as I expect - ios

I'd like to have the coordinates relative to the main window, therefore I'm using convertRect:toView: with nil as second parameter:
CGRect result = [self.view convertRect:addToList.frame toView:nil];
But as a result, I always get
NSRect: {{5, 30}, {35, 35}}
and that are exactly the values I use for generating the addToList-view:
addToList = [UIButton buttonWithType:UIButtonTypeCustom];
addToList.frame = CGRectMake(5.0f, 30.0f, 35.0f, 35.0f);
If I'm adding the addToList-button to a view which is placed in the bottom right corner of the screen, I would expect a CGRect like for example 950, 700, 35, 35 on an iPad landscape, because those are the coordinates of the button relative to the topmost view.
First, I did the conversion in the initWithFrame:-method of a UIViewController; but now, I'm doing it afterwards... what am I missing?
Thanks!
EDIT: is it important that "self.view" is inside a UIScrollView?

If you look at convertRect:toView:'s documentation, you will see that the rect that you pass is defined as within the bounds of the view on which you are calling the method. Since self.view presumably takes the entire window, the rect doesn't change with respect to the window. You should message the parent of the button to get the actual location of the button w.r.t the window.

Related

How to get the position of a uibutton (subview of UITableViewCell) with respect to the whole window iOS objective-C?

As the question says,I want to get the position of a uibutton as shown in the image and I have to show a pop up menu from that uibutton(in the tableviews view).
I went through Question1 and Question2.But these are giving me different locations.
I use the code
CGRect windowRect = [_tableView convertRect:button.frame fromView:tempCell];
temp cell is my selected cell.
I am using kxMenu for popUp menu,and I use the following code.
[KxMenu showMenuInView:_tableView
fromRect:windowRect
menuItems:menuItems];
So,I need to get the windowRect correctly.Anybody have any idea?
If you use
[_tableView convertRect:button.frame fromView:tempCell];
It will convert frame of the button wrt tempcell to button frame wrt _tableView
If you want to get the frame of button wrt the window, you should do either
[button convertRect:button.bounds toView:nil];
or
[button.superView convertRect:button.frame toView:nil];
By default, the nestedView.frame will give you the origin points respective to the immediate parent. Here is the Swift 5 version of this answer:
let frame: CGRect? = nestedView.superview?.convert(nestedView.frame, to: nil)
Objective-C version is as follows:
CGRect frame = [nestedView.superView convertRect:nestedView.frame toView:nil];
This will give you the frame of the nestedView relative to the superview's superview. Hope this helps.

Change the frame afer the CGAffineTransformMakeRotation?

I have added UIView at some angle. Now at the run time i want to move that view to up (say 20px).
At start
dragView = [[DragbleView alloc] initWithFrame:CGRectMake(10, 200, 200, 90)];
dragView.transform = CGAffineTransformMakeRotation (-0.663247);
At Run Time
NSLog(#"Before : %#",NSStringFromCGRect(dragView.frame));
[dragView setCenter:CGPointMake(dragView.frame.origin.x, dragView.center.y+20)];
NSLog(#"After : %#",NSStringFromCGRect(dragView.frame));
Console O/p
Before : {{3.4947295778412979, 147.97225037436704}, {213.0105408443174, 194.05549925126593}}
After : {{-103.0105408443174, 167.97225037436704}, {213.0105408443174, 194.05549925126593}}
As you can see it goes to the wrong place. How to place above 20 px
If you read the docs on the UIView frame property, they say:
If the transform property is not the identity transform, the value of
this property is undefined and therefore should be ignored.
So you can't change the frame once you've changed your view's transform. It doesn't work any more.
Instead you should use the view's center property, as #GaryRiches suggests in his answer.
It is doing as you have specified. You would get better results using the center point for both the X and Y:
[dragView setCenter:CGPointMake(dragView.center.x, dragView.center.y + 20)];
Also, as noted below. iOS uses 0,0 as top left, so to move something "up" would require subtracting 20 from the dragView.center.y.
To move UP, it requires to minimize Y location values, whereas you are adding.
Try this:
[dragView setCenter:CGPointMake(dragView.frame.origin.x, dragView.center.y - 20)];

Can't tap buttons on the left side of 4" iPhone

This is absolutely confusing. On a 3.5" iPhone simulator, all of the UIButtons on my app work just fine. However, when I launch on the 4" iPhone simulator, all of the UIButtons on the left side of the app do not receive any click events.
Below are screenshots of the 3.5" size and the 4" size. On the 4" size, I've added a line. Left of that line, none of the buttons receive click events. To the right of that line, all buttons behave normally. The left side of buttons 2, 5, and 8 do not respond to clicks, but the right sides of those buttons do respond.
UPDATE----
Thanks to #iccir, I've discovered more info. Apparently, my UIWindow is only 320x480 instead of 568x320 as it should be. I'm not touching the UIWindow in my code except to make it key and visible. In my MainWindow.xib I connect its IBOutlet to my rootViewController.
<UIWindow: 0xc097d50; frame = (0 0; 320 480); opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0xc098460>; layer = <UIWindowLayer: 0xc097e70>>
I'm flabberghasted. Any idea why the UIWindow is incorrectly sized?
This is a pretty common issue: Your UIButton is outside of the bounds of one of its superviews. If clipsToBounds/masksToBounds is set to NO (the default), your UIButton is still going to show up, but touch events aren't going to be sent to it.
Let's simplify this case. Suppose a view controller with the following code:
- (void) viewDidLoad
{
[super viewDidLoad];
UIColor *fadedRedColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:0.25];
UIColor *fadedBlueColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:0.25];
CGRect containerFrame = CGRectMake(25, 25, 100, 100);
CGRect buttonFrame = CGRectMake(100, 100, 64, 44);
UIView *container = [[UIView alloc] initWithFrame:containerFrame];
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
[button setTitle:#"Button" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[button setFrame:buttonFrame];
[button addTarget:self action:#selector(_handleButton:) forControlEvents:UIControlEventTouchUpInside];
[container setBackgroundColor:fadedRedColor];
[button setBackgroundColor:fadedBlueColor];
[container addSubview:button];
[[self view] addSubview:container];
}
- (void) _handleButton:(id)sender
{
NSLog(#"Moooooo!");
}
Which looks like this:
The button is contained in container, but it resides outside of the container's bounds (the container is 100 pixels wide and 100 pixels tall, the button's origin is at 100, 100).
When you touch the screen, UIKit is going to start at the top of the view hierarchy (UIWindow) and call -[UIView hitTest:withEvent:] recursively until it finds the view that should handle the touch. However, in this example, UIKit will never descend into the container (since you touched outside its boundary), and thus the button subview will not be hit.
If we instead change the buttonFrame to be 50, 50, it looks like this:
The part of the button that overlaps with the container will respond to touch event. The part that resides outside of the container will not:
To debug a view that isn't fully touchable, you can try a debugging function like the following:
static void sDebugViewThatIsntTouchable(UIView *view)
{
UIView *superview = [view superview];
while (superview) {
CGRect rectInSuperview = [view convertRect:[view bounds] toView:superview];
CGPoint topLeft = CGPointMake(CGRectGetMinX(rectInSuperview), CGRectGetMinY(rectInSuperview));
CGPoint topRight = CGPointMake(CGRectGetMaxX(rectInSuperview), CGRectGetMinY(rectInSuperview));
CGPoint bottomLeft = CGPointMake(CGRectGetMinX(rectInSuperview), CGRectGetMaxY(rectInSuperview));
CGPoint bottomRight = CGPointMake(CGRectGetMaxX(rectInSuperview), CGRectGetMaxY(rectInSuperview));
if (![superview pointInside:topLeft withEvent:nil]) {
NSLog(#"Top left point of view %# not inside superview %#", view, superview);
}
if (![superview pointInside:topRight withEvent:nil]) {
NSLog(#"Top right point of view %# not inside superview %#", view, superview);
}
if (![superview pointInside:bottomLeft withEvent:nil]) {
NSLog(#"Bottom left point of view %# not inside superview %#", view, superview);
}
if (![superview pointInside:bottomRight withEvent:nil]) {
NSLog(#"Bottom right point of view %# not inside superview %#", view, superview);
}
superview = [superview superview];
}
};
Edit:
As you mentioned in the comments, the culprit view was the main UIWindow, which was sized to 320x480 rather than 320x568. Turning on "Full Screen at Launch" in the xib fixed this.
Of course, the question is: "Why?" :)
If you pull up your xib file in a text editor, you will notice that a width of 320 and height of 480 are hardcoded to the window. When the xib is decoded at launch time, the window is initially constructed with this 320x480 frame.
UIKit then queries -[UIWindow resizesToFullScreen] (a private method). If this returns YES, the UIWindow does the equivalent of [self setFrame:[[self window] bounds]].
Toggling the "Full Screen at Launch" flag in Interface Builder directly toggles the private UIWindow.resizesToFullScreen flag.
Let me guess, this happens only in the landscape mode, right ?
I had the same issue in my app when I was developing specifically for the iPhone-4S. But when I began testing on the iPhone-5, touches on the bottom did not work. It were the frame. Make sure frames are set to bounds, both in the code, and the XIB file (if there is one). Different frames/bounds in the man and xib files, might also result in such behaviour.
I eventually removed the xib files, and did everything programmatically. One thing I learnt was, set your frames in viewWillAppear or viewDidAppear methods instead of viewDidLoad. Also check the frame/bounds in your RootViewController. One last thing, try not to use constant value for frames, use referential frames with respect to superview.
PS, one way to know if it really is the frames/bounds that are responsible for this behaviour is, setting the masksToBounds to YES, for the views. That way, your views will not be visible outside their rects.

iOS, setTransform overridden by setFrame? Transform not re-drawing after setFrame is re-run

My transform does not draw after the frame is redrawn with setFrame.
I'm scaling a view when the orientation changes using setFrame. But this view also needs to change position depending on a BOOL argument: On = up in view, off = down off screen. I use setTransform to adjust the position of the view.
First I draw the view by doing a setFrame. This draws the view 100 points just off screen at the bottom. Then I set a transform (-100 on the ty) to bring it up into the view points (as long as the BOOL is set to TRUE, else the transform is 0 and the view is off screen).
[view setFrame:CGRectMake(0, self.view.bounds.size.height, self.view.bounds.size.width, 100)];
[view setTransform:CGAffineTransformMake(1, 0, 0, 1, 0, -100)]
This works fine, as expected, but when change orientation, and this code is re-run (to re-size the view) the transform does not draw, even though it is Logged as being the transform of the view. In other words the view is just off screen, as if the transform.ty was 0.
Log message before re-draw: view.transform.ty -10.000000
Log message after re-draw: view.transform.ty -10.000000
Any help?
A view's frame is a value that is derived from three fundamental values: bounds, center, and transform. The setter for frame tries to do the right thing by reversing the process, but it can't always work correctly when a non-identity transform is set.
The documentation is pretty clear on this point:
If the transform property is not the identity transform, the value of
this property is undefined and therefore should be ignored.
...
If the transform property contains a non-identity transform, the value
of the frame property is undefined and should not be modified. In that
case, you can reposition the view using the center property and adjust
the size using the bounds property instead.
Since your transform only translates the view, I don't see any reason to use it at all. Just change the origin of the view's frame:
[view setFrame:CGRectMake(0, self.view.bounds.size.height - 100, self.view.bounds.size.width, 100)];

iOS - how to add one UIButton with respect to another?

I am an iOS newbie, so please bear with me. I have a blank page with a button centered in it. I want to add another button to the view, just below the centered button. How would I do that? I have added the first button like this -
float x=60, y=200, dy=50;
CGRect frame = CGRectMake(x, y, 200, dy);
UIButton *inboxButton = [[[UIButton alloc] initWithFrame:frame]autorelease];
inboxButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
inboxButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
....
[theView addSubview:inboxButton];
Just calculate the second frame based on the first
CGRect secondFrame = CGRectMake(CGRectGetMinX(frame),
CGRectGetMaxY(frame) + 8.0, // some y padding
CGRectGetWidth(frame),
CGRectGetHeight(frame));
You are not saying if you need that second one centered by itself - I am assuming that you just want it below, left-aligned with the first one:
UIButton *outboxButton = [[[UIButton alloc] initWithFrame:CGRectMake(frame.x,
frame.y + dy + 10.0f,
frame.width,
frame.height)] autorelease];
[theView addSubview:outboxButton];
What I am doing here is simply reusing the horizontal coordinate from the first button. For the vertical coordinate, I am using the original coordinate and add the height and an offset (10.0f) to it. Both, the width and the height are taken from the first button, assuming that they should match in size.
As you will see, there is no way to have this calculation done implicitly, which I assume you actually wanted to find out - that is, by simply supplying some ordering arguments.

Resources