UIStepper is very convenient but I want to change the appearance of it, I need to have customized icon not the plus and minus and also I want to change the color of the control. How can I achieve this? Thanks in advance!
As of iOS 6.0 you can use - (UIImage *)decrementImageForState:(UIControlState)state and - (UIImage *)incrementImageForState:(UIControlState)state to change the labels on the UIStepper control.
This works for iOS 6.0:
[_stepper setDecrementImage:[UIImage imageNamed:#"decrementIcon.png"] forState:UIControlStateNormal];
This works:
[[UIButton appearanceWhenContainedIn:[UIStepper class], nil] setBackgroundImage:[UIImage imageNamed:#"normal.png"] forState:UIControlStateNormal];
[[UIButton appearanceWhenContainedIn:[UIStepper class], nil] setBackgroundImage:[UIImage imageNamed:#"highlighted.png"] forState:UIControlStateHighlighted];
[[UIButton appearanceWhenContainedIn:[UIStepper class], nil] setBackgroundImage:[UIImage imageNamed:#"disabled.png"] forState:UIControlStateDisabled];
Not sure how future proof it is though.
You cannot presently do this. You'll have to write a custom UIControl object if you want to customize the icons.
From the UIStepper Class Reference, the only parameters you can change are
continuous property
autorepeat property
wraps property
minimumValue property
maximumValue property
stepValue property
value property
You cannot customize the icons.
However, since it is a subclass of UIView, you can try changing the backgroundColor.
Here is solution that works for me.
Platform iOS 7.1
[stepper setBackgroundImage:[UIImage new] forState:UIControlStateNormal];
[stepper setDividerImage:[UIImage new] forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal];
The result is only increment, decrement images are visible.
Here's the Swift 4.0 version:
stepper.setDecrementImage(UIImage(named: "yourDecrementImage.png"), for: UIControlState.normal)
stepper.setIncrementImage(UIImage(named: "yourIncrementImage.png"), for: UIControlState.normal)
I also found that shrinking custom images to fit into the stepper buttons would blur them. This is the only extension function I found that properly shrunk the images down without blurring:
extension UIImage {
func resize(targetSize: CGSize) -> UIImage {
return UIGraphicsImageRenderer(size:targetSize).image { _ in
self.draw(in: CGRect(origin: .zero, size: targetSize))
}
}
}
You can always step through the UIViews and guess, too. Anyway, this is my code, which doesn't answer the question directly, but is kind of neat.
#implementation UIStepper (Util)
- (void) fixForIos7 {
if (!IS_IOS7)
return;
for (UIView *view in self.subviews) {
view.backgroundColor = [UIColor whiteColor];
UIButton *button = (UIButton*)view;
[button setTintColor:[UIColor blackColor]];
}
}
#end
This gist I wrote shows creating a StepperControl as a subclass and set custom style of plus/minus.
P.S I have added a label image in the middle as the divider image.
func image(fromView view: UIView) -> UIImage
In regard your question, you can remove the logic for the label and instead just set blank UIImage() for the divider image.
StepperControl.swift
Related
Is there a way to set UIButtons with rounded corners globally like with color below?
[[UIButton appearance] setBackgroundColor:[UIColor purpleColor]];
The list of properties that you can set using UIAppearance is found here:
What properties can I set via an UIAppearance proxy?
Unfortunately rounded corners are not something that is possible.
You could use something like beautify (https://github.com/beautify/beautify-ios) which enhances the UIKit controls to allow you to specify rounded buttons.
With beautify, the following would give you rounded buttons globally:
BYTheme *theme = [BYTheme new];
theme.buttonStyle.border = [[BYBorder alloc] initWithColor:[UIColor blackColor]
width:2.0f
radius:5.0f];
[[BYThemeManager instance] applyTheme:theme];
I've found this link. Please see if it could help.
Taming UIButton
It is using this
[[basicButton layer] setCornerRadius:18.0f];
As i mentioned in previous answer. You have to subclass UiButton for it.. :)
#import <QuartzCore/QuartzCore.h>
Add this in your header file.
then in implementation,
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(100, 100, 100,50);
[btn setTitle:#"Hello" forState:UIControlStateNormal];
[btn setBackgroundColor:[UIColor colorWithRed:128.0/255.0f green:0.0/255.0f blue:0.0/255.0f alpha:0.7]];
btn.frame = CGRectMake(100.0, 100.0, 120.0, 50.0);//width and height should be same value
btn.clipsToBounds = YES;
btn.layer.cornerRadius = 20;//half of the width
btn.layer.borderColor=[UIColor redColor].CGColor;
btn.layer.borderWidth=2.0f;
cornerRadius will do the trick for you.. Let me know if more info needed.. :)
Edit
This cannot be achieved globally. As you used appearence, here is the list to see what you can customize with UIAppearance. what you can do is you can create a subclass of your UIButton, & there you can write implementation of setCornerRadius in initWithCoder Method.
Migrating advice for a workable solution to Swift (it's also possible using an equivalent ObjC category on UIView):
1. Add this extension https://gist.github.com/d3ce2e216884541217d0
2. Code:
let a = UIButton.appearance()
a.layerCornerRadius = 20.0
a.layerBorderColor = UIColor.redColor().CGColor
a.layerBorderWidth = 2.0
This sort of hack works because of how properties are copied. All appearance changes to things like a.layer and a.titleLabel are not propagated, but extension properties are copied.
I have a UIButton which remains selected when I select that button. Here is my code:
- (IBAction)cloudclick:(UIButton *)sender {
UIImage *bgImage = [UIImage imageNamed:#"black_cloud.png"];
UIButton *tmpButton = (UIButton *)[self.view viewWithTag:sender.tag];
[tmpButton setBackgroundImage:bgImage forState:UIControlStateSelected];
}
But it does not remain selected when I click the button. It changes the background but it does not remain as selected button.
you have to write one line after your code
- (IBAction)cloudclick:(UIButton *)sender {
...
[tmpButton setSelected:YES];
}
use this line of code
sender.selected=!sender.selected;
and set different image for diff states at the time alloc if you are making button from code otherwise set images from xib's.
The various states: UIControlStateNormal, UIControlStateSelected, and (UIControlStateSelected | UIControlStateHighlighted) are all actually distinct. If you want your shadowImage to apply both in the (only) highlighted state and in the highlighted+selected state, you must also set:
[button setBackgroundImage: bgImage forState:(UIControlStateHighlighted | UIControlStateSelected)]
in Swift 5
sender.isSelected.toggle()
I'm trying to change the title of an UIButton I've created programmatically, when the user clicks in it. So, this is my code to create the UIButton:
myButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, parentView.frame.size.width, parentView.frame.size.height)];
[myButton setBackgroundColor:[UIColor blackColor]];
[myButton setAlpha:0.7];
[myButton setTitle:#"Hello" forState:UIControlStateNormal];
[myButton addTarget:self action:#selector(userClicked:) forControlEvents:UIControlEventTouchUpInside];
[parentView addSubview:myButton];
And, in my userClicked: method, I do:
-(void) userClicked:(UIButton*)button
{
NSLog(#"USER CLICKED!!!");
if ([NSThread isMainThread])
{
NSLog(#"is main thread");
}
[button setTitle:#"Bye" forState:UIControlStateHighlighted];
[button setTitle:#"Bye" forState:UIControlStateNormal];
[button setTitle:#"Bye" forState:UIControlStateSelected];
[self someLengthyComputation];
}
The weird thing is that I can see the log messages printed:
USER CLICKED!!!
isMainThread
But, the title of the button does not change! What am I doing wrong?
EDIT: Setting the title for several states doesn't work either.
EDIT2: If I print the description of button in the debugger window of Xcode, it shows the right title!
Printing description of button->_titleView:
<UIButtonLabel: 0xa4c9310; frame = (95 216; 130 22); text = 'Bye'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xa44f080>>
This worked for me to update the title text (iOS 7.1, Xcode 5.1):
button.enabled = FALSE;
[button setTitle:#"Test" forState:UIControlStateNormal];
button.enabled = TRUE;
I was having the same problem, and I noticed that everywhere else I was setting the attributedTitle. So any updates to the title were not affecting the attributed title.
My solution:
[button setAttributedTitle:#"" forState:UIControlStateNormal];
instead of
[button setTitle:#"" forState:UIControlStateNormal];
At the moment, the shortest work around I came with is calling:
[button setNeedsLayout];
After updating the title.
This seems to happen in iOS 7.1.
All my buttons, which were behaving correctly in previous iOS versions (or maybe just compiled with previous SDKs) suddenly stopped doing that when compiled in Xcode 5.1.1 SDK 7.1.
Looks like Apple's bug.
I had a similar problem using storyboards. Using the answers above I had to use
[mybutton setTitle:#"SomeText" forState:UIControlStateNormal];
[button setNeedsLayout];
[button layoutIfNeeded];
AND I had to make sure that the button type was 'Custom' not 'System' in the attributes inspector.
Please see if this might help you...when the button is clicked check for condition if buttonToggled...like below when you have a function like changeButtonText
-(IBAction)changeButtonText:(id)sender {
if (!buttonToggled) {
[sender setTitle:#"Initial Text" forState:UIControlStateNormal];
buttonToggled = YES;
} else {
[sender setTitle:#"Alternative Text" forState:UIControlStateNormal];
buttonToggled = NO;
}
}
There are several issues in your code:
You are assigning callback to the button:
#selector(userClicked:)
but your code is in another method:
-(void)userTapOnTapToRefreshView:(UIButton*)button
To fix that you need to implement something like this:
-(void)userClicked:(id)sender
{
[(UIButton*)sender setTitle:#"Bye" forState:UIControlStateNormal];
}
Also this part of code does not make sense for me:
[parentView myButton];
Try to change it to:
[parentView addSubview:myButton];
Hope it will help :)
For Swift 3 to 5 just use the following:
button.setTitle("My title", for: .normal)
or for a attributed text use this:
button.setAttributedTitle(<AttributedString>, for: .normal)
This is kinda late and somewhat relates to Walter Schurter's response:
I had a similar issue where my button text was being set correctly until I updated to 7.1. Then I found that since my button was disabled, I had to set the title color and title text for the disabled state to get the text to show up. Then everything worked like a charm!
In iOS 7, UIButton's title is not updated when it is disabled. It seems like a bug on iOS 7.
As a workaround, update both normal and disabled title. It works!
[button setTitle:title forState:UIControlStateNormal];
[button setTitle:title forState:UIControlStateDisabled];
Try this:
[button setTitle:#"Bye" forState:UIControlStateHighlighted];
or:
[button setTitle:#"Bye" forState:UIControlStateSelected];
You could also modify your void function:
-(void) userClicked
{
NSLog(#"USER CLICKED!!!");
if ([NSThread isMainThread])
{
NSLog(#"is main thread");
}
[myButton setTitle:#"Bye" forState:UIControlStateNormal];
}
In my case I tried everything but nothing was working.
Then I just changed button type from system to custom from storyboard. BOOM! everything started behaving normally.
Change the button type to 'Custom', instead of 'System' and it will work as expected :)
Well, is all about the enabled state of a UIButton, Apple has changed something in 7.1 that does not allow you to change the title if you have the UIButton on a disabled state, thats all.
Thanks Apple, i have lost 10 min. debuging an app that was working fine.
Just found out this morning, got updated XCode yesterday to the 5.1.1 and iOS to 7.1
It could be the button layout refresh issue.....
Try using...
[button setNeedsLayout];
[button layoutIfNeeded];
It will force button to update the layout.
Finally, I've figured it out. There were two problems:
1) button was not in state UIControlStateNormal.
2) I was calling a method performing a long computation just after setting the title, [self someLengthyComputation].
I've solved the problem by:
1) Setting the title for all states of the button.
2) Performing that big computation in another thread, not the main thread.
My working code now is:
-(void) userClicked:(UIButton*)button
{
NSLog(#"USER CLICKED!!!");
if ([NSThread isMainThread])
{
NSLog(#"is main thread");
}
[button setTitle:#"Bye" forState:UIControlStateHighlighted];
[button setTitle:#"Bye" forState:UIControlStateNormal];
[button setTitle:#"Bye" forState:UIControlStateSelected];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[self someLengthyComputation];
});
}
Thank you very much to everybody who has answered/commented!
This may be trivial, but if you set some button image (instead of background image) which fills the whole button frame, this will shift the title right and thus make it invisible.
If so, try changing button image to background image.
Per Apple developer guide, you should not set either the button title label text or color directly as a property (for example, do not do this: myButton.titleLabel.text=#"SomeText" or myButton.titleLabel.textColor=[UIColor blackColor]).
Rather, you set them using the UIButton setter functionality, like this:
[myButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
...or like this:
[myButton setTitle:#"SomeText" forState:UIControlStateNormal];.
See the Apple guide.
Conclusion after trying many solutions is to use setAttributedTitle instead of setTitle.
To make the title string for AttributedString:
NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:#" new Test "];
[button setAttributedTitle:attString forState:UIControlStateNormal];
By the way, this problem is not occasionally happen in normal but it suddenly happen mainly for the following reasons:
If you change the enabled state of a UIButton and try to change the title.
If you use an attributed value and then want to change it using setTitle, the attributed value is superior to the title in that case.
If you navigate to another view controller and then return back to update the button title.
There is no requirement to use:
[button setNeedsLayout];
[button layoutIfNeeded];
Instead, first set type as DEFAULT to CUSTOM
If you applied setAttributedTitle then use:
[button setAttributedTitle:[NSAttributedString new] forState:UIControlStateNormal];
Otherwise there is no need to change anything.
If the color of the text has not changed then apply same thing and set title color with:
[button setTitleColor:[any_color] forState:UIControlStateNormal];
None of the above worked for my case (I was having a button on top of a view presented with UIPopoverController), and I had to removeFromSuperview before setTitle, then addSubview back - hope this helps someone with similar issues
I doubt if 'button' passed in as a parameter is myButton.Anyway,if myButton is a member var,you can just [myButton setTitle:#"Bye" forState:UIControlStateNormal];
Try to declare you button as a property, either in the interface or in the implementation part of you view / view controller
#property(nonatomic,strong) UIButton* myButton;
then create it
self.myButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, parentView.frame.size.width, parentView.frame.size.height)];
[_myButton setBackgroundColor:[UIColor blackColor]];
...
It should work. Don't ask me why really - I just think it's all about ARC / Modern memory management..
EDIT:
It should also work if you keep a reference on the button in your implementation..
#implementation myViewController
{
UIButton* myButton;
}
-(void)createButton
{
myButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, parentView.frame.size.width, parentView.frame.size.height)];
[myButton setBackgroundColor:[UIColor blackColor]];
...
}
...
instead of
yourButton.titleLabel?.text = "T"
use this
yourButton.setTitle("TITLE", for: .normal)
Always do addSubview before setting the text on UIButton.
I'm trying to set the title for a custom button that I've created programmatically. The button's image and the frame come up fine, but the title doesn't. I can't really think of anything wrong with this code, so any help is apreciated!
self.helpButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.helpButton setFrame:CGRectMake(113.0, 685.5, 73.0, 40.0)];
UIImage *helpImg = [UIImage imageNamed:#"11_HelpCancel_Up.png"];
[self.helpButton setImage:helpImg
forState:UIControlStateNormal];
[self.helpButton setTitle:#"Help" forState:UIControlStateNormal];
[self.helpButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
// [self.helpButton setFont:[UIFont boldSystemFontOfSize:14.0]];
[self.view addSubview:self.helpButton];
Thanks,
Teja.
Use
[self.helpButton setBackgroundImage:helpImg forState:UIControlStateNormal];
- setImage:forState: seems to override - setTitle:forState:
Was having trouble with the button title showing up on my storyboard when I switched the type of button from system -> custom. The problem was the color of the text changed to white when I made the change so anyone having a similar issue, make sure to check the color of the button text.
firstButton is a UIButton of type Custom. I'm programmatically putting three of them across each cell of a table, thusly:
[firstButton setImage:markImage forState:UIControlStateNormal];
[firstButton setContentMode:UIViewContentModeScaleAspectFit];
[cell.contentView addSubview:firstButton];
Elsewhere, I'm telling it to clipToBounds. What I get is a crop of the center square of the image, rather than an aspect-scaled rendering of it. I've tried this lots of ways, including setting the mode property on firstButton.imageView, which also doesn't seem to work.
I had the same problem. I see this question is a little old, but I want to provide a clear and correct answer to save other folks (like me) some time when it pops up in their search results.
It took me a bit of searching and experimenting, but I found the solution. Simply set the ContentMode of the "hidden" ImageView that is inside the UIButton.
[[firstButton imageView] setContentMode: UIViewContentModeScaleAspectFit];
[firstButton setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
Perhaps that's what Dan Ray was alluding to in his accepted answer, but I suspect not.
If you're dealing with the UIButton's image (as opposed to it's backgroundImage), setting the contentMode on the UIButton itself or on its imageView has no effect (despite what other answers say).
Alternatively do this instead:
self.button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
self.button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
Or size your image accordingly.
OR just use a UIImageView (which properly respects contentMode) with a UITapGestureRecognizer attached to it, or a transparent UIButton on top of it.
Rather than setting the contentMode on the button itself, you'll want to set contentHorizontalAlignment and contentVerticalAlignment properties and (crucially) the contentMode for the button's imageView for any kind of aspect fill or fit:
button.contentHorizontalAlignment = .fill
button.contentVerticalAlignment = .fill
button.imageView?.contentMode = .scaleAspectFit
You can also do other things like aligning the button's image to the top. If you don't need an aspect fill or fit, you just can set the alignment by itself:
button.contentVerticalAlignment = .top
After a couple of hours of confusion, here's how I got it to work under iOS 3.2. As dusker mentioned, using setBackgroundImage instead of setImage did the job for me.
CGRect myButtonFrame = CGRectMake(0, 0, 250, 250);
UIImage *myButtonImage = [UIImage imageNamed:#"buttonImage"];
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeCustom];
[myButton setBackgroundImage:myButtonImage forState:UIControlStateNormal];
[myButton setFrame: myButtonFrame];
[myButton setContentMode: UIViewContentModeScaleAspectFit];
The answer is to use a UIImageView with all the lovely Content Mode settings you want, and then layer a custom button on top of it. Dumb that you can't do that all in one shot, but it appears that you can't.
These two things (which are quite hard to find initially) will stretch your UIButton image to fit the button size:
one should always try to set such in the Storyboard rather than code.
Found a fix for this. Set the adjustsImageWhenHighlighted property of UIButton to NO.
UIButton *b = [[UIButton alloc] initWithFrame:rect];
[b setImage:image forState:UIControlStateNormal];
[b.imageView setContentMode:UIViewContentModeScaleAspectFill];
[b setAdjustsImageWhenHighlighted:NO];
Hope this helps. Feel free to comment below, I will follow up on any questions that you have.
My answer is similar to Kuba's. I needed my image to be programatically set.
UIImage *image = [[UIImage alloc] initWithContentsOfFile:...];
[button setBackgroundImage:image forState:UIControlStateNormal];
button.imageView.contentMode = UIViewContentModeScaleAspectFill; //this is needed for some reason, won't work without it.
for(UIView *view in button.subviews) {
view.contentMode = UIViewContentModeScaleAspectFill;
}
Only solution which worked for me:
[button setImage:image forState:UIControlStateNormal];
button.imageView.contentMode = UIViewContentModeScaleAspectFill;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
Swift 3
self.firstButton.imageView?.contentMode = .scaleAspectFill
For anyone experiencing this on iOS 15 and Xcode 13, see Matt's answer in this other question.
The behavior of Xcode changed and now defaults UIButtons from the library to the plain style, which prevents the child image from scaling as expected.
Instead of setImage try setBackgroundImage
I believe we have a simple interface builder issue here - apparently the IB ignores any content-mode changes AFTER you have set the image-property.
the solution is as simple: set the content mode, remove previously set image-names (make sure you remove it in all states, default, highlighted etc.), then re-enter the desired image-names in all desired states - et voilĂ .
I also advice to have a look at the adjustsImageWhenHighlighted UIButton property to avoid weird deformations of the image, when the button is pressed.
In trying to figure this out, my method got a bit hackier as time went on, and I wound up subclassing UIButton and overriding setHighlighted:
For me it works to just knock down the image alpha to .5, because they're on a black background.
However, it only works if I comment out [super setHighlighted:] (where it appears the image-stretchy code is going on), which just doesn't feel like the right way to solve this at all...everything seems to be working fine, though. We'll see how it holds up as I keep working on it.
- (void)setHighlighted:(BOOL)highlight {
if (highlight) {
[self.imageView setAlpha:.5];
} else {
[self.imageView setAlpha:1];
}
// [super setHighlighted:highlight];
}
If anyone looking for answer that work in iOS 6 and iOS 7 and storyboard:
You can set image in your storyboard:
And then:
for(UIView* testId in self.subviews) {
if([testId isKindOfClass:[UIImageView class]])
[testId setContentMode:UIViewContentModeScaleAspectFill];
}
If the UIButton does not seem to listen to the layout constraint settings, do check whether the images are larger than the button size. Always use the #2x and #3x images for retina resolutions.