hitTest based on center of UISegmentmentedControl returns UIToolbar - objective-c

I'm experimenting with some code which probes a UI to see it's structure. As part of this I am doing a hitTest on a UISegmentedControl which is embedded in a UIToolbar at the bottom of the screen. I have a structure like this:
UIToolbar
UISegmentedControl
UISegment
...
I get the centre of the UISegmentedControl and hit test it like this:
UIView *view = // code which gets a reference to the UISegmentedControl.
frameInWindow = [view.window convertRect:view.frame fromView:view.superview];
CGPoint locationInWindow = CGPointMake(
frameInWindow.origin.x + 0.5 * frameInWindow.size.width,
frameInWindow.origin.y + 0.5 * frameInWindow.size.height);
UIView *target = [view.window hitTest:locationInWindow withEvent:nil];
I've been over this numerous times and I cannot see why it sets target to the UIToolbar instead of the UISegmentedControl. I would have expected target to be the same control. Or some UIView inside the segmented control.

The docs say:
"This method ignores view objects that are hidden, that have disabled
user interaction, or have an alpha level less than 0.01. This method
does not take the view’s content into account when determining a hit.
Thus, a view can still be returned even if the specified point is in a
transparent portion of that view’s content."
Is user interaction with your segmented control enabled?

Related

UIVIew from XIB with Autolayout to UItableView Header

I am writing because I have a problem with the Auto Layout.
I'm trying to create a simple view in InterfaceBuilder with Auto Layout I want to load code and enter as a header of a table (not as header section). I explain briefly what are the characteristics.
The imageView must be square and must be as wide as the screen.
The space under the picture to the bottom of view that contains the button and label must be high 50 points.
Between image and button has to be a fixed distance of 12 points.
Between image and label must be a fixed distance of 13 points.
All these features are able to get them with Auto Layout. I added a constraint to the aspect ratio of the image (1: 1) and the various constraints for distances. all right.
The real problem is that by launching the app on iphone 6+ simulator (414 points of width), the image (with the label and button) goes above the cells.
Enabling various transparencies I noticed that the superView of Image View, only increase the width. It does not increase its height! How do I fix?
This is the code:
- (void)viewDidLoad{
//...
PhotoDetailsHeaderView *hView = (PhotoDetailsHeaderView *)[[[NSBundle mainBundle] loadNibNamed:#"PhotoDetailsHeaderView" owner:self options:nil] objectAtIndex:0];
hView.delegate = self;
self.tableView.tableHeaderView = hView;
//...
}
This is how I create the xib:
and this is how it is on the simulator, the green box is Uiimageview and the yellow box (under green box) is the mainview (or superview):
How can fix it?
Many thanks to all!
You'll need to add a property to store your PhotoDetailsHeaderView:
#property (nonatomic, strong) PhotoDetailsHeaderView *headerView;
Then calculate its expected frame in viewDidLayoutSubviews. If it needs updating, update its frame and re-set the tableHeaderView property. This last step will force the tableView to adapt to the header's updated frame.
- (void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
CGRect expectedFrame = CGRectMake(0.0,0.0,self.tableview.size.width,self.tableView.size.width + 50.0);
if (!CGRectEqualToRect(self.headerView.frame, expectedFrame)) {
self.headerView.frame = expectedFrame;
self.tableView.tableHeaderView = self.headerView;
}
}
The problem is probably that in iOS you have to reset the header of the table view manually (if it has changed its size). Try something along these lines:
CGRect newFrame = imageView.frame;
imageView.size.height = imageView.size.width;
imageView.frame = newFrame;
[self.tableView setTableHeaderView:imageView];
This code should be in -(void)viewDidLayoutSubviews method of your view controller.

How to dynamically reposition UIButton as subview of UIImageView when rotating

I'm working on an iPad app that lets you control different things in a prototype of an intelligent house. For example it lets you turn lights on and off. For this, I have made a UIImageView that shows the floor plan of the house and added UIButtons as subviews for each lamp that can be toggled.
As you can see the buttons are placed perfectly on the floor plan using the setFrame method of each UIButton. However, when I rotate the iPad to portrait orientation, the following happens:
The buttons obviously still have the same origin, however it is not relative to the repositioning of the image.
The floor plan image has the following settings for struts and springs:
and has its content mode set to Aspect Fit.
My question is
how do I dynamically reposition each UIButton, such that it has the same relative position. I figure I have to handle this in the {did/should}AutorotateToInterfaceOrientation delegate method.
It should be noted that the UIImageView is zoomable and to handle this I have implemented the scrollViewDidZoom delegate method as follows:
for (UIView *button in _floorPlanImage.subviews) {
CGRect oldFrame = button.frame;
[button.layer setAnchorPoint:CGPointMake(0.5, 1)];
button.frame = oldFrame;
button.transform = CGAffineTransformMakeScale(1.0/scrollView.zoomScale, 1.0/scrollView.zoomScale);
}
Thank you in advance!
I find the best way to layout subviews is in the - (void) layoutSubviews method. You will have to subclass your UIImageView and override the method.
This method will automatically get called whenever your frame changes and also gets called the first time your view gets presented.
If you put all your layout code in this method, it prevents layout fragmentation and repetition, keeps your view code in your views, and most things just work by default.

Enable user interaction on a UIButton underneath a UIVew?

In my program I have configured a UIView to be placed over a number of UIBUttons. This is so you can pan across the view and have the buttons move over as well as selecting the buttons themselves. However, it is not possible to select the buttons when they are underneath this UIView. Does anybody know how to configure a UIButton's userInteractionEnabled while it is under these conditions?
This technique has worked for me - caveat: I just typed this in it's not compiled or nuthin it's given as a starting point.
Create the buttons and place them at the top of the view hierarchy (i.e. where they interact normally and respond to taps, on top of the UIView)
When creating the buttons - keep a reference to them in an array.
NSMutableArray* arrayOfPeskyButtons;
Keep a record of the button frames
NSMutableArray* arrayOfPeskyFrames = [NSMutableArray mutableArray];
for ((UIButton*)button in arrayOfPeskyButtons)
{
[arrayOfPeskyFrames addObject:[NSValue valueWithCGRect:button.frame];
}
When your app responds to an action on the UIView - panning or if you change it to a UIScrollView (probably best btw), then move the frame of the buttons correspondingly so that they appear fixed in place to the user.
I have used this technique to create 'overlays' of buttons which sit on top of scrollviews and it works surprisingly well. Simply in the scrollView delegate method.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
take note of the scrollviews offset and adjust each of the frames of the buttons accordingly depending on the offset of the scrollView
CGPoint offset = scrollView.contentOffset;
Iterate through the array of buttons and move them.
int index = 0;
for ((UIButton*)button in arrayOfPeskyButtons)
{
CGRect originalFrame = [[arrayOfPeskyFrames objectAtIndex:index]CGRectValue];
CGRect currentFrame = button.frame;
//adjust frame
//e.g.
currentFrame.origin.x = originalFrame.origin.x + offset.x;
button.frame = currentFrame.;
index++;
}
Instead of changing the value of the UIButton's userInteractionEnabled, you should set the userInteractionEnabled value of the UIView on top of the UIButton to no.
When the UIView's userInteractionEnabled is set to YES, the UIView was 'eating up' the events. The button would then never receive any events.

How do you determine the size/frame of a custom UINavigationItem.titleView?

After creating a custom view and assigning it to the navigationItem.titleView property it is displayed like this
with the custom view filling the space between the two buttons. Therefore, the custom view is not centered on the navigation bar. How do I determine the frame of the view in the .titleView property? I want to center some text in the navigation bar, say under the time stamp.
If you really want to get titleView's frame (in your top-level view's coordinate space), you can do this:
[self.navBar layoutIfNeeded];
CGRect titleViewFrameInTopLevelViewSpace = [self.navigationItem.titleView
convertRect:self.navigationItem.titleView.bounds
toView:self.view];
You need to do layoutIfNeeded if you have just assigned titleView, because by default the navigation bar won't lay out its subviews until the next pass through the run loop.
That said, the titleView will be centered automatically, if it fits. I think you are setting the frame (or bounds) of your custom view too large. I tested this two ways:
I set up the titleView directly in the XIB. I simply dragged a View from the Object library onto the center of the navigation bar:
It sized the view to 128x33 automatically. The resize handles let me adjust the size. It stays centered until it overlaps the Categorize button. Then it shifts left.
I set the titleView property in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 33)];
customView.backgroundColor = [UIColor greenColor];
self.navItem.titleView = customView;
}
The result looks like this:
You could get the width of the leftBarButtonItem and the rightBarButtonItem after you've set them, and then use that to determine how to centre within the view you supply to titleView. That might do what you want?

location of a button/textfield in a view in cocoa

I'm sorry to ask such a question, but I can't seem to find the answer for it from the wild web.
I want to find out the location of a NSTextField in my custom view, so I can add other textfields under it programmatically.
The reason I don't just add other textfields in IB, is because I need to be able to dynamically create them.
The NSTextField is linked to an IBOutlet.
So the main question is: How do I find out the location of a NSTextField (or NSButton, it doesn't really matter atm) in a custom view (I need to get the coordinates of the item)?
Make an instance Variable with the NSTextField and then call these methods on it to get the four values you want.
NSRect textFieldFrame = [textField frame];
NSPoint textFieldLocation = textFieldFrame.origin;
NSSize textFieldSize = textFieldFrame.size;
NSInteger x = textFieldLocation.x;
NSInteger y = textFieldLocation.y;
NSInteger width = textFieldSize.width;
NSInteger height = textFieldSize.height;
Every kind of view or control (like NSButton, NSTextField, etc.) that inherits from NSView has a -frame method that contains the positioning information.
If your custom view has an IBOutlet connected up to the NSTextField, you could do the following:
NSRect textFieldFrame = [textField frame];
That NSRect contains all of the information for where the text field is positioned.
See the following 2 guides for more information:
View Programming Guide: Understanding a View's Frame and Bounds
Cocoa Drawing Guide: Coordinate Systems and Transforms
Any view has both a bounds and a frame property. bounds is the rectangle that encloses the view in that view's own coordinate space; frame is the enclosing rectangle in the superview's coordinate space. In this case, you want the text view's frame, i.e. its location and size in the view that contains it, so that you can locate other text views in the same superview.