I want to add a view and a button programmatically like follows.
The problem is that the button does not react on clicking. I mean neither does it get highlighted or calls the selector.
The reason is that I want to implement a list row for a recording (a sound file). The list row should be selectable for drill-down and have a play button. So I got a RecordingView subclassing UIView that itself adds the button using a target from the constructor. See code below.
If anyone has a better approach to do this that could also be a solution.
#implementation MyViewController
- (IBAction) myAction {
RecordingView *recordingView = [[RecordingView alloc] initWithFrame:CGRectMake(30, 400, 130, 50)withTarget:self];
[recordingView setUserInteractionEnabled:YES];
[[self view] addSubview:recordingView];
}
#implementation RecordingView
- (id)initWithFrame:(CGRect)frame withTarget:(id) target
{
self = [super initWithFrame:frame];
UIButton *playButton = [[UIButton alloc] initWithFrame:CGRectMake(185, 5, 80, 40)];
[playButton setTitle:#"Play" forState:UIControlStateNormal];
[playButton setTitleColor:[UIColor darkTextColor]forState:UIControlStateNormal];
// creating images here ...
[playButton setBackgroundImage:imGray forState: UIControlStateNormal];
[playButton setBackgroundImage:imRed forState: UIControlStateHighlighted];
[playButton setEnabled:YES];
[playButton setUserInteractionEnabled:YES];
[playButton addTarget: target
action: #selector(buttonClicked:)
forControlEvents: UIControlEventTouchDown];
[self addSubview:playButton];
return self;
}
When I add the button the same way, directly in the view controller .m-file, the button does react on clicking. So there is something about the RecordingView. What do I need to do different here?
Also, are there any better ways to provide the target and selector for the touch event?
You may simply need to set userInteractionEnabled to YES on your RecordingView.
Another problem is that you are creating the RecordingView with a frame width of 130, but you are setting the X-axis origin of playButton to 185. This means playButton is entirely outside of the bounds of its superview. The default value for clipsToBounds is NO, so the button is drawn anyway. But touch events will never reach the button, because they are rejected when the system hit-tests the RecordingView.
This is from the hitTest:withEvent: documentation in UIView Class Reference:
Points that lie outside the receiver’s bounds are never reported as hits, even if they actually lie within one of the receiver’s subviews. This can occur if the current view’s clipsToBounds property is set to NO and the affected subview extends beyond the view’s bounds.
You need to either make RecordingView's frame wider, or move playButton to be inside the bounds of its superview.
Related
This code worked in a UIView container, but after changing that container to a UIViewController, the code no longer works:
-(void)viewDidLoad
{
[super viewDidLoad];
[self.view setUserInteractionEnabled:YES];
// set button
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(60, 30, 220, 220);
[button setImage:self.obj.img forState:UIControlStateNormal];
[button addTarget:self action:#selector(selected:) forControlEvents:UIControlEventTouchUpInside];
[button setUserInteractionEnabled:YES];
[self.view addSubview:button];
}
-(void) selected:(UIButton *) button // also tried (id) sender
{
NSLog(#“Button Selected“);
}
These containers are created programmatically (dynamically), from web services, so I cannot use Storyboard. The reason for changing to a UIViewController is that I need a segue to another screen. Also, these containers are placed in a UIScrollView.
Everything else is working properly, except the selector.
Setting the userInteraction wasn't necessary earlier, but I've gone ahead and tried that in different configurations. I've added this to the scrollview, the viewController, and the button, just for good measure.
I even tried some online tutorials for programmatically creating UIButtons in UIViewControllers and was able to do that just fine. Not sure what the issue is here. Thanks.
I have a program that dynamically generates UIButtons in the center of
the screen with push of another button.
The buttons are not updating the x-coordinates when I rotate the device.
Here is my code for creating buttons:
- (IBAction)createButton:(UIButton *)sender {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setBackgroundColor:[UIColor redColor]];
[button addTarget:self action:#selector(doSomething:) forControlEvents:UIControlEventTouchUpInside];
button.frame = CGRectMake(self.xCoord,self.yOffset,100.0,120.0);
[self.view addSubview:button];
_yOffset = _yOffset+130;
}
The XCoord and self.yOffset were set in viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
self.yOffset = 109;
self.xCoord = self.view.bounds.size.width/2-50;
}
The coordinates of the button in your code are fixed once they've been added to the view. If you want to update the frame of the buttons when the screen rotates, you'll need to trigger the repositioning of the buttons when the screen rotates.
AutoLayout in a storyboard makes this easier (if you just want a button to appear then you can add it in a storyboard and change its hidden property from NO to YES when the other button is pressed -- this won't be helpful if you want to add an arbitrary number of buttons, although you could have a hidden placeholder view that you use as a positioning reference)
If you want to keep the buttons x-position, you need to add Auto Layout constraints. There are a couple ways to do this programmatically. You can use the NSLayoutConstraint object directly or use Auto Layout's Visual Format Language. For more info, see Apple's documentation.
I have(/had) a UITextField subclass which has a custom delete button as a rightView property.
This is working fine up until I rotate the device and then the button does not react anymore.
I changed my approach and 'cheated' a bit. Now I'm adding the UIButton on top of the UITextField in code. Which also works fine up until I rotate the device. I checked with backgroundColors if there are no other elements on top of the button.
I also tried removing the button and adding it again when the device rotates. It's visible but the action does not trigger. If I start my app in Portrait or in Landscape it works up until I rotate the device.
How I add the button:
if (!btn_delete)
{
btn_delete = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width - 70, (tf_search.frame.size.height / 2) - 8, 17, 16)];
[btn_delete setImage:[UIImage imageNamed:#"icon_cross_textfield"] forState:UIControlStateNormal];
[btn_delete addTarget:self action:#selector(clearTextField:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn_delete];
[self.view bringSubviewToFront:btn_delete];
}
else
{
[btn_delete removeFromSuperview];
btn_delete = nil;
}
Method -(void)clearTextField:(UIButton*)sender does not get called after rotation.
Any advice is appreciated.
The UIButton was added to a UIViewController's UIView which was part of a UIView of another UIViewController. They all had clearColour as backgroundColor so the UIButton was still visible but not in the bounds of the superView.
I have a UIButton2 that is added on to UIButton1:
The action #selector only gets called where UIButton2 intersects UIButton1 (the "b"). So, a tap in the "a" or "c" area, the action method (addDockButtonTouchedDown) does NOT get called, but it does when you tap in the "b" section. Here's some code:
// button1 is created elsewhere in the code and is a subclass of UIButton for UI layout (has a footer label and title label set). nothing fancy going on. button1 userInteractionEnabled is set to yes
// button2 is below...
NSString *dockPath = [[NSBundle mainBundle] pathForResource:#"AddToMyDock" ofType:#"png"];
UIImage *dockImage = [UIImage imageWithContentsOfFile:dockPath];
CGRect rect = CGRectMake(-20.0f, -10.0f + dockImage.size.height+1, dockImage.size.width, dockImage.size.height);
UIButton *button2 = [[UIButton alloc] initWithFrame:rect];
button2.userInteractionEnabled = YES;
button2.exclusiveTouch = YES;
[button2 setBackgroundImage:dockImage forState:UIControlStateNormal];
[button2 addTarget:self action:#selector(addDockButtonTouchedDown:)
forControlEvents:UIControlEventTouchDown];
[button1 addSubview:button2];
Any ideas why this is happening and how to fix it??? Please don't suggest to simply make button1 bigger.
First of all, adding a button as a subview of another button isn't really common design and I would avoid doing that. Simply create a container UIView that holds both buttons.
The problem you're seeing is because while your button may be displayed beyond the bounds of its superview (the other button), actions on the outside of button1's bounds won't be registered. The only reason you can see button2, is because clipsToBounds is set to NO on button1, which allows its subviews to be drawn outside of its bounds.
So, the only way to make your current solution work is indeed by making button1 bigger. However, the better option would be to create superview that's big enough to hold both buttons, and add both buttons to it.
It not works on "a" and "c" regions because the hit test for the touch event fails there when called for button1.
You should subclass UIButton for button1 and override the hit test with something like this:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if(CGRectContainsPoint(self.childButton.frame, point)) {
return [self.childButton hitTest:[self convertPoint:point toView:self.childButton] withEvent:event];
}
return [super hitTest:point withEvent:event];
}
Note: childButton is button2 in your example.
Note2: I think that however this should work, the ugliness of this is a sign of a design problem, but that's your business.
I have a coreplot project. The class of the view in viewcontroller has been changed to CPTGraphHostingView.
After creating the graph (this works fine),
I am adding a button as a subview , this launches a popover
the button shown is inverted. I have the same control in different view controllers and it works fine. The Popover also appears fine.
Here is the code that creates and adds the UIButton
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self
action:#selector(monthSelection)
forControlEvents:UIControlEventTouchDown];
[button setTitle:#"Change Month" forState:UIControlStateNormal];
button.frame = CGRectMake(330, 650, 111, 40);
[self.view addSubview:button];
[here is a screenshot of whats happening. UIButton is suppose to show somewhere below the popover. What am i missing.
Do not add subviews to the graph hosting view. Instead, make other views siblings of the hosting view, i.e., add them as subviews of the same superview.