UIButton *rideButton = [UIButton buttonWithType:UIButtonTypeCustom];
rideButton.titleLabel.font = CUSTOM_BUTTON_FONT;
[rideButton setTitleColor:[UIColor CUSTOM_BUTTON_TITLE_COLOR1] forState:UIControlStateNormal];
[rideButton setTitleColor:[UIColor CUSTOM_BUTTON_TITLE_COLOR2] forState:UIControlStateHighlighted];
[rideButton setTitle:#"Ride" forState:UIControlStateNormal];
[rideButton addTarget:self action:#selector(rideButtonClicked) forControlEvents:UIControlEventTouchUpInside];
CGSize s1 = [rideButton.titleLabel.text sizeWithFont:rideButton.titleLabel.font];
rideButton.frame = (CGRect) {
.size.width = (s1.width+rideBarButtonGap)>100?100:(s1.width+rideBarButtonGap),
.size.height = s1.height + rideBarButtonGap
};
rideButton.backgroundColor = [UIColor redColor];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 11.0)
{
[[rideButton.widthAnchor constraintEqualToConstant:rideButton.frame.size.width] setActive:YES];
[[rideButton.heightAnchor constraintEqualToConstant:rideButton.frame.size.height] setActive:YES];
}
UIBarButtonItem *rideBarButton= [[UIBarButtonItem alloc] initWithCustomView:rideButton];
self.navigationItem.rightBarButtonItem = rideBarButton;
This is my code to create custom UIBarButtonItem. I have set the background color to red just to see the bounds of the button. The background color looks perfectly outside the text but I'm unable to click the button properly. The button's action get fired only when 'Ri' is selected. If 'de' is selected, the function is not called. It happens only in iOS 11. I have added width and height constraints to fix this but there is no change.
EDIT:
When I increase the width of widthAnchor, I'm able to acheive what I want but the button moves left and theres lot of gap between the button and Screen. So I tried to align the content using rideButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;. Though the content moves right, the clickable area is still in the middle.
Try this:
public extension UIView {
//From iOS 11 -> UIBarButtonItem uses autolayout instead of dealing with frames. so adding contraint for the barbuttonItem
public func applyNavBarConstraints(size: (width: CGFloat, height: CGFloat)) {
if #available(iOS 11.0, *) {
let widthConstraint = self.widthAnchor.constraint(equalToConstant: size.width)
let heightConstraint = self.heightAnchor.constraint(equalToConstant: size.height)
heightConstraint.isActive = true
widthConstraint.isActive = true
}
}}
and use it as below:
rideBarButton.applyNavBarConstraints(size: (width: 50, height: 44)) //expected height and width
This works for me. Hope this helps!
Related
I'm creating a button and adding it as a UIBarButtonItem in the code below. I'm trying to make it a perfect circle.
UIButton *hasMessageBtn = [UIButton alloc];
hasMessageBtn.backgroundColor = [UIColor orangeColor];
hasMessageBtn.layer.frame = CGRectMake(0, 0, 24, 24);
hasMessageBtn.layer.cornerRadius = 12;
[hasMessageBtn addTarget:self action:#selector(openMessages:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *messages = [[UIBarButtonItem alloc] initWithCustomView:hasMessageBtn];
self.navigationItem.leftBarButtonItem = messages;
It looks like the size is wrong in two ways, though. The height never changes, and even thought the width changes when I change the width value, it seems to be incorrect because setting corner radius,
hasMessageBtn.layer.cornerRadius = 12;
doesn't completely close the rounded corners on the top and bottom. What am I missing here?
When you takes smaller the default size of custom view, its height will stretch the height to 34. That's why your leftBarButton getting distorted.
Solution:
Apply height and width constraint to barButton (Suggested *)
UIBarButtonItem *messages = [[UIBarButtonItem alloc] initWithCustomView:hasMessageBtn];
[NSLayoutConstraint activateConstraints:#[[messages.customView.widthAnchor constraintEqualToConstant:24.0], [messages.customView.widthAnchor constraintEqualToConstant:24.0]]];
Make your custom view's button size, CGSize(34, 34)
hasMessageBtn.layer.frame = CGRectMake(0, 0, 34, 34);
hasMessageBtn.layer.cornerRadius = hasMessageBtn.frame.size.height / 2;
As of iOS 11 you must set constraints to bar button items. Here is how it looks in objective-c:
[hasMessageBtn.widthAnchor constraintEqualToConstant:24].active = YES;
[hasMessageBtn.heightAnchor constraintEqualToConstant:24].active = YES;
And in swift:
hasMessageBtn.heightAnchor.constraint(equalToConstant: 24).isActive = true
hasMessageBtn.widthAnchor.constraint(equalToConstant: 24).isActive = true
If you have more inquiries about this topic I suggest you to look at 2017 WWDC video Updating Your App for iOS 11
I really need your help with this one (first post on SO -- be gentle):
I have two dynamic UIButtons which I would like to have centered in a UIView, which in turn should be centered in a UINavigationbar and UIToolbar. I can't - despite a lot of Googling - seem to figure out a proper way to do this.
This is what I've done so far:
In viewDidLoad, I add the two buttons as subviews and set the view as the UINavigationbar's titleView
self.myClass.viewForTitleAndButton = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 120, 32)];
[self.myClass.viewForTitleAndButton setBackgroundColor:[UIColor blackColor]];
[self.myClass.viewForTitleAndButton addSubview:self.myClass.myButton];
[self.myClass.viewForTitleAndButton addSubview:self.myClass.myOtherButton];
self.navigationItem.titleView = self.myClass.viewForTitleAndButton;
In a method being triggered when I press certain buttons, I set the title (and bounds) of one of the buttons depending on what's clicked:
CGSize titleSize = [title sizeWithAttributes:#{NSFontAttributeName : [UIFont italicSystemFontOfSize:17.0]}];
CGSize screenSize = [UIScreen mainScreen].bounds.size;
CGFloat newX = (screenSize.width - titleSize.width) / 2;
CGRect buttonFrame = self.myClass.myButton.frame;
//Removing the line below doesn't do any difference at the moment
self.myClass.myButton.bounds = CGRectMake(newX, buttonFrame.origin.y, titleSize.width+8, buttonFrame.size.height);
[self.myClass.myButton setTitle:title forState:UIControlStateNormal];
NSLog(#"Title: %#", title);
//title is a NSString that changes depending on what is clicked. I am 100% sure it changes as I can see it in the log every time the method is triggered.
The problem is that the title of myButton is not changed. It worked before with the very same button when it was placed in a different spot and not as a subview.
Q1: What am I missing to make the title of the button change?
Q2: Is adding the buttons as subViews to a view that is then placed in the navigationbar and toolbar respectively a sound way to accomplish what I want?
This is really bugging me, any pointers in the right direction is much appreciated.
I don't think you can add a button which is already an outlet of a view controller. Thinking in another way, a button(view) can only has one superview.
Create button dynamically for the title view.
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *iv = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 160, 44)];
iv.backgroundColor = [UIColor greenColor];
titleButton = [UIButton buttonWithType:UIButtonTypeCustom];
titleButton.frame = CGRectMake(0, 0, 120, 44);
[titleButton setTitle:#"test" forState:UIControlStateNormal];
[titleButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[iv addSubview:titleButton];
self.navigationItem.titleView = iv;
}
//some event to change the button title
- (void)click:(UIButton *)button
{
[titleButton setTitle:#"I changed" forState:UIControlStateNormal];
}
I have a carousel menu of UIButtons in my app. Everything works great except that the buttons are kinda big and it won't scroll when a finger is dragged across the button. The dragging has to be done outside of the button that's currently front and center in the carousel. Is there some setting I can change so that I can swipe across the buttons to scroll and have them also be interactable?
Here's the code that creates the buttons:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(0, 0, 500.0f, 320.0f);
button.layer.cornerRadius = 8.0f;
button.layer.masksToBounds = NO;
button.layer.borderWidth = 0.0f;
button.layer.shadowColor = [UIColor blackColor].CGColor;
button.layer.shadowOpacity = 0.8;
button.layer.shadowRadius = 12;
button.layer.shadowOffset = CGSizeMake(12.0f, 12.0f);
[button setImage:(UIImage*)[buttons objectAtIndex:index] forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
Make sure UIScrollView property canCancelContentTouches is set to YES.
scrollView.canCancelContentTouches = YES;
Moreover, a UIScrollView implements a method touchesShouldCancelInContentView.
The default returned value is YES if view is not a UIControl object;
otherwise, it returns NO.
This means that UIScrollView does not attempt to cancel touches in UIButtons which prevents scrolling. Additionally, you can subclass UIScrollView and override touchesShouldCancelInContentView to return YES when content view object is a UIButton.
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.
Is it possible to adjust the x,y position for the titleLabel of a UIButton?
Here is my code:
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn setFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
[btn setTitle:[NSString stringWithFormat:#"Button %d", i+1] forState:UIControlStateNormal];
[btn addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
btn.titleLabel.frame = ???
//make the buttons content appear in the top-left
[button setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
[button setContentVerticalAlignment:UIControlContentVerticalAlignmentTop];
//move text 10 pixels down and right
[button setTitleEdgeInsets:UIEdgeInsetsMake(10.0f, 10.0f, 0.0f, 0.0f)];
And in Swift
//make the buttons content appear in the top-left
button.contentHorizontalAlignment = .Left
button.contentVerticalAlignment = .Top
//move text 10 pixels down and right
button.titleEdgeInsets = UIEdgeInsetsMake(10.0, 10.0, 0.0, 0.0)
Swift 5
button.contentHorizontalAlignment = .left
button.contentVerticalAlignment = .top
button.titleEdgeInsets = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 0.0, right: 0.0)
The easiest way to do it visually is to use the attribute inspector** (appears when editing a xib/storyboard), setting the "edge" property to title, adjusting it's insets, then setting "edge" property to image, and adjusting accordingly.
It's usually better than coding it , since it's easier to maintain and highly visual.
Derive from UIButton and implement the following method:
- (CGRect)titleRectForContentRect:(CGRect)contentRect;
Edit:
#interface PositionTitleButton : UIButton
#property (nonatomic) CGPoint titleOrigin;
#end
#implementation PositionTextButton
- (CGRect)titleRectForContentRect:(CGRect)contentRect {
contentRect.origin = titleOrigin;
return contentRect;
}
#end
For my projects I've this extension utility:
extension UIButton {
func moveTitle(horizontal hOffset: CGFloat, vertical vOffset: CGFloat) {
self.titleEdgeInsets.left += hOffset
self.titleEdgeInsets.top += vOffset
}
}
This can be done in xib. Select your button, go to "Show the Size Inspector" tab and setup insets.