iOS Button Click Area Inconsistent - ios

I have a back button in a custom header. Sometimes the click area is the entire button. Sometimes the click area is a small part of the bottom of the button. Any ideas what might cause this? If I use a tap gesture, the gesture recognizer stops firing an event.
Here's what it looks like. The red is the button. The blue is the header container. The green is the header label inside of the content layout.
Initialize my header view. You can see that the button is on the top layer. Everything else gets added to the content view, so any added elements shouldn't interfere with the clickabilitiy.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
CGFloat statusBarHeight = 0.0;//for iOS below 7.0
if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;;
}
self.contentLayout = [[UIView alloc] initWithFrame:CGRectMake(0.0, statusBarHeight, frame.size.width, frame.size.height - statusBarHeight)];
[self addSubview:self.contentLayout];
CGFloat height = 44.0 < self.contentLayout.frame.size.height ? 44.0 : self.contentLayout.frame.size.height;
self.ivBack = [[UIImageView alloc] initWithFrame: CGRectMake(15.0, (height - 15.0) / 2.0, 11.5, 15.0)];
[self.ivBack setImage:[UIImage imageNamed:#"back.png"]];
[self.ivBack setUserInteractionEnabled:YES];
[self.ivBack setHidden:YES];
[self.contentLayout addSubview:self.ivBack];
self.btnBack = [UIButton buttonWithType:UIButtonTypeCustom];
[self.btnBack setAccessibilityLabel:#"btnBack"];
[self.btnBack setFrame: CGRectMake(0.0, 0.0, 50.0, self.frame.size.height)];
[self addSubview:self.btnBack];
self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;
self.layer.shadowColor = [[UIColor colorWithRed:123.0/255.0 green:123.0/255.0 blue:123.0/255.0 alpha:1.0] CGColor];
self.layer.shadowOffset = CGSizeMake(0, 2.0);
self.layer.shadowRadius = 4.0;
self.layer.shadowOpacity = 0.7;
[self.btnBack setBackgroundColor:[UIColor redColor]];
[self.contentLayout setBackgroundColor:[UIColor blueColor]];
}
return self;
}
Enabling the button and assigning it a click event.
-(void)addBackTarget:(id)target selector:(SEL)selector {
[self.ivBack setHidden:NO];
[self.btnBack addTarget:target action:selector forControlEvents:UIControlEventTouchDown];
}

I have no idea why the above solution was so fickle. The button fit in its container. The container fit in its view. There was nothing in front of the button. To get the back button to work consistently, I made my header a subclass of UINaviationBar. In the subclass I added a UIBarButtonItem to the topItem (I had to initialize this first). Seems to be working great now.

Related

iOS : Keyboard input accessory view wrong orientation when we start the application in landscape mode

In my iOS application most of the screens are in portrait mode and we have only one screen to show in landscape mode. So when I am starting the application in the landscape mode then the frame of accessory view is set according to the landscape mode while the other components are set according to the portrait mode so due to which the accessory view get separated from the keyboard across the application.
This issue arised after iOS 11 and moreover In iPhone X the issue is not replicable.
- (id)initWithItems:(NSArray *)items {
self = [self initWithFrame:CGRectMake(0.f, 0.f, 0.f, 44.f)];
if (self) {
// Set style
if ([ThemeManager currentThemeID] == LightThemeID) {
self.borderMask = BorderTop|BorderBottom;
self.borderWidth = 1.f;
self.borderColor = [UIColor lightGrayColor];
self.backgroundColor = [UIColor colorWithRed:217.f/255.f green:220.f/255.f blue:226.f/255.f alpha:1.f];
}
else {
UIToolbar *fakeToolbar = [[UIToolbar alloc] initWithFrame:self.bounds];
[fakeToolbar setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
[fakeToolbar setBarStyle:UIBarStyleBlackOpaque];
[self addSubview:fakeToolbar];
}
// Create scroll view
_scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
[_scrollView setShowsHorizontalScrollIndicator:NO];
[_scrollView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
[self addSubview:_scrollView];
// Add snippet buttons to scroll view
CGFloat nextButtonX = 7.f;
for (NSString *item in items) {
UIButton *itemButton = [self snippetButtonWithTitle:item];
CGRect frame = itemButton.frame;
frame.origin.x = nextButtonX;
itemButton.frame = frame;
nextButtonX += frame.size.width + 7.f;
[_scrollView addSubview:itemButton];
}
// Set scroll view content size
[_scrollView setContentSize:CGSizeMake(nextButtonX, 44.f)];
}
return self;
}
- (BOOL)enableInputClicksWhenVisible {
return YES;
}
- (UIButton *)snippetButtonWithTitle:(NSString *)title {
UIButton *button = [InputAccessorySnippetBarButton buttonWithType:UIButtonTypeCustom];;
[button setTitle:title forState:UIControlStateNormal];
UIFont *buttonFont;
CALayer *buttonLayer = button.layer;
// Set button font
[button.titleLabel setFont:buttonFont];
// Set the button's frame width to fit the content
CGFloat titleWidth = [title sizeWithAttributes:#{NSFontAttributeName:buttonFont}].width;
button.frame = CGRectMake(0.f,
(44.f - 30.f) / 2,
titleWidth + 20.f,
30.f);
// Set button action
[button addTarget:self
action:#selector(buttonTapped:)
forControlEvents:UIControlEventTouchUpInside];
return button;
}
- (void)buttonTapped:(id)sender {
[[UIDevice currentDevice] playInputClick];
UIButton *button = sender;
id<InputAccessorySnippetBarDelegate> strongDelegate = self.delegate;
[strongDelegate snippetBar:self
tappedItemWithTitle:[button titleForState:UIControlStateNormal]];
}

UIWindow addSubview handle events

I want to build a custom alert by UIViewController and add its view as a subview to the current window. The view displays very well, but I can't handle any touch events.
I have tried many times to add a button to this viewcontroller and add a target. I have also tried adding a UITapGestureRecognizer and set view's userInteractionEnabled to true, but this also failed even when I cleared all subviews just left the button.
Have I missed something?
Here is the code:
CustomAlert Viewcontroller:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor clearColor];
backgroundView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
backgroundView.backgroundColor = [UIColor blackColor];
backgroundView.alpha = 0.5;
[self.view addSubview:backgroundView];
backImage = [[UIImageView alloc]initWithFrame:CGRectMake((self.view.frame.size.width - 300) / 2, (self.view.frame.size.height - 250) / 2, 300, 250)];
backImage.image = [UIImage imageNamed:#"AlertBackground"];
[self.view addSubview:backImage];
confirmButton = [[UIButton alloc]initWithFrame:CGRectMake( backImage.frame.origin.x + 100 , backImage.frame.origin.y + 250 - 40, 100, 26)];
[self.view addSubview:confirmButton];
[confirmButton addTarget:self action:#selector(confirmButtonClick:) forControlEvents:UIControlEventTouchUpInside];
confirmButton.backgroundColor = [UIColor redColor];
[confirmButton setTitle:#"click me" forState:UIControlStateNormal];
}
-(void)confirmButtonClick:(UIButton*)sender{
[self.view removeFromSuperview];
}
-(void)show{
UIWindow * window = (UIWindow*)[UIApplication sharedApplication].keyWindow;
[window addSubview:self.view];
}
Method call custom alert:
CustomAlert * alert = [[CustomAlert alloc]init];
[alert show];
Try to rewrite your -show{} method with these
UIWindow * window = (UIWindow*)[UIApplication sharedApplication].keyWindow;
[window.rootViewController.view addSubview:self.view];
UPDATE:
And if previous don't help, try to overwrite
-(void)show{
UIWindow * window = (UIWindow*)[UIApplication sharedApplication].keyWindow;
[window.rootViewController addChildViewController:self];
[window.rootViewController.view addSubview:self.view];
[self didMoveToParentViewController:window.rootViewController];
}
Those three methods establish parent-child relations.

Add a view at the top of another view iOS

I need help. I'm designing my UI, and I have been using the properties FRAME and CENTER to layout my views. Both of those are so much helpful to me. I also use the technique of adding my elements as subview of UIViews container.
But, right now, I want to place a UIView at the top of another. Please take a look at my picture. The buttons at the bottom of the screen is already set and is perfect right now, I'm only having a hard time to place my UIView (a container of my textfields) above the button/divider.
I'm only starting in iOS, I think 1 month plus already. I know basics of constraints programmatically but I don't want to use this for now as much as possible.
PS. I make my screen programmatically.
Sample of my code in this screen
// start adding container
UIView *container2 = [[UIScrollView alloc] init];
container2.frame = CGRectMake(0, 0, [TPRConstants screenWidth] * 0.95, heightOfScrollView);
container2.backgroundColor = [UIColor blueColor];
container2.contentSize = CGSizeMake([TPRConstants screenWidth] * 0.95, 250);
[self.view container2];
// stuffs and subviews/textfields inside the container
self.phoneTextField = [[UITextField alloc] init];
_phoneTextField.frame = CGRectMake(0, 200, scrollView.frame.size.width, 50);
_phoneTextField.delegate = self;
_phoneTextField.borderStyle = UITextBorderStyleNone;
_phoneTextField.background = [UIImage imageNamed:#"textfieldLogIn.png"];
_phoneTextField.backgroundColor = [UIColor clearColor];
[_phoneTextField setKeyboardType:UIKeyboardTypeNumberPad];
[container2 addSubview:_phoneTextField];
// add container for divider and log in button
UIView *container = [[UIView alloc] init];
container.frame = CGRectMake(0, 0, [TPRConstants screenWidth], 80);
container.backgroundColor = [UIColor whiteColor];
container.center = CGPointMake([TPRConstants screenWidth] * 0.50, [TPRConstants screenHeight] - container.frame.size.height/2);
[self.view addSubview:container];
// add divider
UIImageView *divider = [[UIImageView alloc] initWithFrame:CGRectMake(0, 10, container.frame.size.width, 5)];
divider.image = [UIImage imageNamed:#"dividerCP.png"];
divider.backgroundColor = [UIColor clearColor];
divider.contentMode = UIViewContentModeCenter;
[container addSubview: divider];
// add save login
UIButton *saveBtn = [UIButton buttonWithType:UIButtonTypeCustom];
saveBtn.frame = CGRectMake(0, 0, container.frame.size.width * 0.95, 50);
saveBtn.center = CGPointMake(container.frame.size.width /2 , (container.frame.size.height - saveBtn.frame.size.height/2) - 10 );
[saveBtn addTarget:self action:#selector(saveBtnClick:) forControlEvents:UIControlEventTouchUpInside];
[saveBtn setBackgroundImage:[UIImage imageNamed:#"logoutupCP.png"] forState:UIControlStateNormal];
[saveBtn setTitle:#"Save" forState:UIControlStateNormal];
[saveBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[saveBtn setTitleColor:[UIColor blackColor] forState:UIControlStateHighlighted];
[saveBtn setBackgroundImage:[UIImage imageNamed:#"logoutdownCP.png"] forState:UIControlStateHighlighted];
[container addSubview: saveBtn];
In iOS everything in a view is added as a stack i.e on top of each other. So your button will always be on top of the divider as it is added last.
If you try your solution now on several screens, you will find that all of the items will be in different positions.
So you have to add constraints.
The easiest way is via storyboard.
Keep your textfields in one view and your button and divider in another view and add a vertical spacing constraint between both views.
Note: Ofcourse you have to add all the required trailing, leading, top and bottom constraints on each textField and the container views and separator image in view plus center horizontally and vertically constraints to the button.
If you wish to place a UIView at the base of phoneTextField use the maximum y position of phoneTextField for the y origin position of the UIView.
UIView *newView = [[UIView alloc] initWithFrame:CGRectMake(0,CGRectGetMaxY(phoneTextField.frame)+0,[TPRConstants screenWidth],50);
newView.backgroundColor = [UIColor blueColor];
[self.view addSubview:newView];
Then change the saveBtn frame to make space for newView:
saveBtn.frame = CGRectMake(0, CGRectGetMaxY(newView.frame), container.frame.size.width * 0.95, 50);

UIButton's title property logs correctly but not showing up when adding button as subview

I have a UIButton that I have created, and I can successfully add it as a subview and see it on the screen. The problem is, when I try using:
[myButton setTitle:#"My Title" forState:UIControlStateNormal];
The button does not show a title when I run the app. If I NSLog the button's title after using the above method, it has in fact been set to the title that I'm passing in.
Here's my code. This is inside a UIView subclass. I realize that some of this logic might not belong in a UIView subclass but I'm just trying to get this to work as fast as possible:
// Set the view's frame, background color, corner radius
self.frame = CGRectMake(45, 200, 235, 187);
self.backgroundColor = [UIColor whiteColor];
self.layer.borderWidth = 1;
self.layer.borderColor = [UIColor grayColor].CGColor;
self.layer.cornerRadius = 5;
// Create the popup's body text label
self.popupBodyTextLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 100, 150)];
self.popupBodyTextLabel.text = #"Text goes here.";
// Add text view to popup's subviews
[self addSubview:self.popupBodyTextLabel];
// Create ok button
UIButton *myButton = [[UIButton alloc]initWithFrame:CGRectMake(0, 143, 120, 44)];
myButton.backgroundColor = [UIColor whiteColor];
myButton.layer.borderWidth = 1;
myButton.layer.borderColor = [UIColor grayColor].CGColor;
[myButton setTitle:#"OK" forState:UIControlStateNormal];
// Round the button's bottom left corner
UIBezierPath *myButtonMaskPath = [UIBezierPath bezierPathWithRoundedRect:myButton.bounds byRoundingCorners:(UIRectCornerBottomLeft) cornerRadii:CGSizeMake(5.0, 5.0)];
CAShapeLayer *okButtonMaskLayer = [[CAShapeLayer alloc] init];
myButtonMaskLayer.frame = myButton.bounds;
myButtonMaskLayer.path = myButtonMaskPath.CGPath;
myButton.layer.mask = myButtonMaskLayer;
// Add selector to button
[myButton addTarget:self action:#selector(myButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
// Add button to subviews
[self addSubview:myButton];
// Create the background view
self.backgroundView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
self.backgroundView.backgroundColor = [UIColor grayColor];
self.backgroundView.alpha = 0.5f;
// Show our new views
[[[UIApplication sharedApplication] keyWindow] addSubview:self.backgroundView];
[[[UIApplication sharedApplication] keyWindow] addSubview:[MPPopupView shared]];
The code for setting button title is right, so use this code to set title color
[myButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];

My UIView has a UIButton as one of it's subviews but not responding to events in objective-c

Here is my code. It has started to look crowded after an hour or 2 of trying to solve this issue. I've tried setting user interaction enabled to yes, I've tried just using a button alone and not making it the subview of another view.
Right now I have filterBar > filterBarContainer > filterButton.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.navigationController.navigationBar.userInteractionEnabled = YES;
self.view.userInteractionEnabled = YES;
// Create filter bar with specified dimensions
UIView *filterBar = [[UIView alloc] initWithFrame:CGRectMake(0, 42, self.view.frame.size.width, self.navigationController.navigationBar.frame.size.height)];
[filterBar setUserInteractionEnabled:YES];
// Make it a subview of navigation controller
[[[self navigationController] navigationBar] addSubview:filterBar];
[filterBar setBackgroundColor:[[UIColor whiteColor] colorWithAlphaComponent:0.9f]];
[filterBar setTag:1];
// Reusable vars
// CGFloat filterBarX = filterBar.frame.origin.x;
//CGFloat filterBarY = filterBar.frame.origin.y;
CGFloat filterBarHeight = filterBar.frame.size.height;
CGFloat filterBarWidth = filterBar.frame.size.width;
// Create centre filter button with specified dimensions
UIView *filterButtonContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 2, filterBarWidth / 2 - 30 , filterBarHeight - 10)];
[filterButtonContainer setUserInteractionEnabled:YES];
// Make it a subview of filter bar
[filterBar addSubview:filterButtonContainer];
[filterButtonContainer setBackgroundColor:[UIColor redColor]];
// Centre the button
[filterButtonContainer setCenter:[filterBar convertPoint:filterBar.center fromView:filterBar.superview]];
// Add button to filter button
UIButton *filterButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[filterButton setUserInteractionEnabled: YES];
[filterButton setFrame:CGRectMake(0, 0,40 , 20)];
[filterButton setTitle:#"test" forState:UIControlStateNormal];
[filterButtonContainer addSubview:filterButton];
[filterButton setBackgroundColor:[UIColor yellowColor]];
// self.filterButton = filterButton;
[filterButton addTarget:self action:#selector(filterButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
NSLog(#"dd");
filterButtonContainer.layer.borderColor = [UIColor blackColor].CGColor;
filterButtonContainer.layer.borderWidth = 1;
This is the method I'm trying to trigger.
- (void)filterButtonTapped:(UIButton *)sender
{
NSLog(#"filter button tapped");
UIActionSheet *filterOptions = [[UIActionSheet alloc] initWithTitle:#"Filter Garments"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:#"Recommended", #"What's New", #"Price - High to Low", #"Price - Low to High", nil];
[filterOptions showInView:self.collectionView];
}
Most likely one of your parent views (superviews of your button) have a width or height that places the filter button outside of their area. The button is still displayed (if you don't define that the view should cut off subviews), but elements outside the view's area will not get any touch events. Check your superview's width and height.
You're adding filterBar as a subview of your navigationBar, but you're setting it's y-origin within navigationBar to 42. And then you're setting your filterButtonContainer's y-origin within the filterBar to 2. Assuming navigationBar's height is 44, most of the filterBar and the entirety of your filterButtonContainer and filterButton won't be within their navigationBar superview so the button wouldn't be selectable.
Edit: To make it selectable while keeping the positioning intact, I suggest adding the filter bar to self.view. For example, change:
[[[self navigationController] navigationBar] addSubview:filterBar];
to
[self.view addSubview:filterBar];

Resources