trouble with UIAppearance and UIButton subclassing - ios

I have a custom button, that is just a standard UIButton, but with a CAGradientLayer added in.
In my custom button, I have defined two properties:
#property (nonatomic, strong) UIColor* topColor UI_APPEARANCE_SELECTOR;
#property (nonatomic, strong) UIColor* bottomColor UI_APPEARANCE_SELECTOR;
If those two values get set, the my button draws itself with a nice linear gradient. Works great.
I also like to put as much into InterfaceBuilder as possible. So, on some of these buttons, in IB's "Identity Inpsector" I add in "User Defined Runtime Attributes" for these properties. Again, works great.
Next, I thought I'd try using UIAppearance proxies. Most of my custom gradient buttons all have the same colors. But there are a few that are different. So, I figured what I would do is use the appearance-proxy stuff to set the default colors for this class, and then for any buttons that are different, I could just set their values in IntefaceBuilder. This fails.
Apparently, what's happening is that it's reading the runtime attributes from my storyboard file first, but afterwards those values get overwritten by the appearance proxy. I wouldn't expect this to work this way, but it does.
Any tips on how to accomplish this? Or should I just give up on the runtime attributes thing?

OK, I've thought about this, and I guess this is really what the Appearance proxy is supposed to do. So, my solution is to have two classes "MySpecialButton" and "MyAppearanceButton".
MyAppearanceButton will be a sub-class of MySpecialButton.
The look of "MyAppearanceButton" will be controlled by the appearance proxy calls. If I want a button that isn't controlled that way, I'll make a "MySpecialButton" and set the properties in the User Defined Runtime Attributes. That should do it.

Related

IBInspectable property set at design time not keeping value

We are in the process of implementing IBInspectable into a large app in hopes of allowing some settings to be set in Interface Builder to reduce the amount of code in our views. I don't have much experience with IBInspectable/IBDesignable and am looking for some answers and/or clarification on what I'm doing wrong.
I have declared a property in a UITableViewCell subclass as follows:
#property (nonatomic,strong) IBInspectable UIColor* backgroundColor;
When declaring the property like this, I get an option to set that color in Interface Builder > Attributes Inspector, which is to be expected. However when I set the color, the value for _backgroundColor is nil at runtime.
[_labelLoginBackground setBackgroundColor:_backgroundColor];
Could someone clarify what might be going here? Thanks!
UITableViewCell is a subclass of UIView, which already contains a property named "backgroundColor". Do 1 of the following:
Rename your own "backgroundColor" property to "loginBackgroundColor" and start debugging from there.
OR
Do not create a redundant property. Set the background color using the selection widget that is already present in Interface Builder before you ever add IBInspectable.

Adding custom protocol to UITextField

I have some forms for the authentications and signup views and I want that all UITextField inside those forms have a UIButton as an accessory view, just above the keyboard. I want to have the possibility to set the title and the action for this button
Because I want all those text field have one and each will have a title and an action, and to avoid redundancy, I thought about a protocol.
I want something like extending a custom protocol, for example UITextFieldAccessoryViewDelegate and to be conform to some functions like :
-buttonAccessoryView title ... -> String
-didClickOnAccessoryViewButton.. -> ()
My mind is closed. Someone can give me some ideas to do what I want ?
You could use associated objects to solve this problem. This lets you add a property and its synthesized getter/setters.
#interface UITextField(AccessoryButton)
#property(readwrite, strong, nonatomic) UIButton *accessoryButton;
#end
#import<objc/runtime.h>
#implementation UITextField(AccessoryButton)
-(UIButton*) accessoryButton
{
objc_getAssociatedObject(self, #selector(accessoryButton));
}
-(void) setAccessoryButton:(UIButton *)accessoryButton
{
objc_setAssociatedObject(self, #selector(accessoryButton), accessoryButton, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
Include this category into your forms that need the UIButton for the text fields. Then assign action and title to a UIButton like how you normally do.
Considering everything you want to do I think you are better off using a subclass of UITextField rather than an extension.
Extensions can't add instance variables to the objects they extend, but subclasses can. As lead_the_zeppelin points out in his answer, you can use associated objects to simulate adding instance variables with an extension but it seems like you're making it needlessly complicated when a subclass gives you everything you want.

Connect two labels to one outlet

Now I understand that this question has been asked before, but the answers were unsatisfactory. My issue is that I have a view controller with a view and stuff in it including a label. I added a bunch of code for it and now I'm expanding on it. I now have an issue where I've decided to add another UIView to my interface and it has a label and that label is going to function EXACTLY like a label I have in my first UIView. My problem is that I don't want to have to go in my view controller method and add another line of code each time I manipulate that first label. Is there anyway I can link another label to my initial IBOutlet I have set for my first label? Or do I have to go in my code and add an extra line of code everytime I manipulate that first label?
It depends on what you want to do to that label. If you're looking to change some of the attributes of the label in the same way (e.g., font, text colour, alignment) then you can put both labels in an IBOutletCollection and iterate over the collection in your view controller.
If you want to have different data in the label, but other attributes the same, then you'll need a separate IBOutlet for that label.
You can combine the two techniques as well. e.g.
(interface)
#property (weak, nonatomic) IBOutlet UILabel *firstName;
#property (weak, nonatomic) IBOutlet UILabel *lastName;
#property (strong, nonatomic) IBOutletCollection(UILabel) NSArray *labels;
(implementation)
- (void)viewDidLoad {
[super viewDidLoad];
for (UILabel *aLabel in self.labels) {
// Set all label in the outlet collection to have center aligned text.
[aLabel setTextAlignment = NSTextAlignmentCenter;
}
[self.firstName setText:#"First Name"];
[self.lastName setText:#"Last Name"];
}
Basically the simple answer is no. Whether you use outlets or an outlet collection or tags or whatever, you ultimately have one reference in your code to one label in your interface, and another reference in your code to another reference in your interface. You can compress your mode of expression so as to cycle readily through those references (as suggested in a different answer), but the basic fact is inescapable that, ultimately, the only way to "talk to" a label is through the one reference that points to that label and to that label alone.
The only way of getting around that is not to use direct references at all. For example, a single message can be sent to multiple recipients by using an NSNotification. So you could have two instances of some UILabel subclass of your own, and "shout" to both instances simultaneously by posting a notification from your view controller - the notification is then automatically passed on to both labels, because you have arranged beforehand for them to register for it.
Similarly, another alternative is that you could use key-value observing so that a change in your view controller is automatically propagated to both labels automatically because they "observe" the change, meaning they are sent notifications - really just an inverted form of NSNotification. (If this were Mac OS X, you could make a simpler, safer version of this arrangement by using "bindings".)
However, I really cannot actually recommend that approach. The truth is that we still live in an excruciatingly primitive world of text-based programming, one line at a time, one command at a time, one reference at a time, and we must just bite the bullet and get on with it.
Swift 3, Xcode 8
Create a prototype cell with objects
then add another prototype
It will copy the objects from the first prototype cell.
The new objects will be connected to the same IBOutlet
Also, copy and pasting objects maintains IBActions, but does not maintain IBOutlets.
I hope this answers your question, as none of the other answers had this work around.

Subclassing vs Category with Interface Builder

I have read multiple times that we should not subclass a component (a UIButton for example) :
Why shouldn't I subclass a UIButton?
Subclassing a UIButton
The problem is when I use Interface Builder.
For example, I have a button with a precise appearance in a lot of my views. I can set them each time with IB (it's painful), or I can use a custom class to factorize the custom behavior and appearance.
It seems a bit contradictory to me that the only way to simplify the process with IB is to do it the way that everybody recommends against.
Is there a better solution ? Can I use a category with IB ?
Thanks.
You might be able to use the UIView appearance proxy. I don't know what all you're doing to your buttons but this might help:
Put this is your AppDelegate file in the application:didFinishLaunchingWithOptions: method
if([UIButton conformsToProtocol:#protocol(UIAppearanceContainer)]){
[[UIButton appearance] setBackgroundImage:[UIImage imageNamed:#"YourImage"] forState:UIControlStateNormal];
//modify any other UIButton properties you want to set globally
}
The second link you provided was pretty clear, and this is pretty much what apple itself states, subclass, but never mess with the internal structure.
Best example is iOS 7, now things are completely different and, for example, an application I'm maintaining had a subclassed UIControl, and now it has trouble running on the new iOS, simply because, it was built with assumptions on how the internal structure works (iterating the internal subviews replacing some things). You might not get your app rejected, but it will be a pain in the a** to maintain.
As a rule of thumb, anything you can do to an UIButton from the outside, something like this:
[myButton setBackgroundImage:... forState:...];
[myButton setTextColor:... forState:...];
myButton.titleLabel.font = ...
You can move it to the inside of a custom subclass method:
+ (UIButton*)fancyPantsButton
{
UIButton *button = [UIButton butonWithType:UIButtonTypeCustom];
[myButton setBackgroundImage:... forState:...];
[myButton setTextColor:... forState:...];
myButton.titleLabel.font = ...
return button;
}
You can also do this on init or awakeFromNib without problems (and I usually prefer the later).
UIAppearence is also an option, as was suggested by user hw731. Whatever floats your boat, really.
As for the second question, nib files pretty much create instance a class and then fill-in the things it stores using setValue:forKey: when loading (that's why you get an error like "class is not key-value compliant for something" when you screw up a nib), so if something is categorised when the nib is being loaded, then yes, nibs respect categories, as its simply using initWithCoder.. and then filling in the gaps.
And, by the same token, the nib file won't be able to fill-in custom properties, since it doesn't know about them, unless you explicitly add them on the "User Defined Runtime Attributes" in IB (iOS 5 onwards).
Another technique for nibs, is using
#property (strong) IBOutletCollection(UIButton) NSArray *buttons;
And then iterating and customising buttons accordingly (be it via a subclass, category, local method, ...). This method is really helpful if you want just a handful of custom buttons, but not enough to warrant using a subclass.
I don't see any reason that you shouldn't subclass UIButton, especially for your purpose of making configuration with IB easier. Neither of the links you provided explain why you shouldn't subclass, so their assertions don't seem reliable. On the other hand, the presence of UIButtonTypeCustom in UIButton.h gives the impression that the framework authors planned for UIButton subclasses.

How to programmatically set UISlider initial value, or, how does an iOS program start up?

I have been developing in xCode for exactly 3 days now. The last time I did UI development it was in Windows 3.1 with the Petzold book. Regardless of that I have my first iOS app up and running and it basically does what I need. I have two sliders to select hue and saturation, and in response to them I dynamically draw a bunch of gradient shaded circles. I got that much running between the Hello World example and stackoverflow, including caching the gradient in CGLayer (so thanks to all the stackoverflow people). There is one little piece that I can't quite get right though:
I want to set the initial value of one slider to 1.0 instead of the default 0.5. I know I can do that in IB, but I prefer to code it and I think I'd like to move away from IB altogether. I don't really understand how it makes connections between things and I'd like to understand the code. I have code like this to try to set it:
- (void)viewDidLoad
{
NSLog(#"viewDidLoad");
[super viewDidLoad];
[hue_slider setValue:0.5];
[sat_slider setValue:1.0];
self.led_view.hue_slider_value=0.5;
self.led_view.sat_slider_value=1.0;
// Do any additional setup after loading the view, typically from a nib.
}
sat_slider still ends up in the middle instead of at the end (1.0 is the max value). From stackexchange reading I understand that I am probably calling this at the wrong time, that the slider hasn't really been loaded when viewDidLoad is called, and my initial value is overwritten by the one specified in IB. What I haven't seen though is where the call should be made. So the question:
Where in my program should I put
[sat_slider setValue:1.0];
so that it sets the initial value of the slider, overwriting the default in IB? Can someone explain the order of how things start up in an iOS program? And a pointer to online or printed books regarding iOS and Objective C programming would be great.
Edit
When I check the value of sat_slider it is nil. So that means a connection is missing? I dragged it in the storyboard and created an IBOutlet in addition to an action.
#interface led_testViewController : UIViewController
- (IBAction)saturation_scroll:(id)sender;
- (IBAction)hue_scroll:(id)sender;
#property (retain, nonatomic) IBOutlet UISlider *hue_slider;
#property (retain, nonatomic) IBOutlet UISlider *sat_slider;
#property (strong, nonatomic) IBOutlet led_view *led_view;
#end
You may put the code in - (void)viewWillAppear:(BOOL)animated;
I followed the suggestions of ABC and NJones to check sat_slider and it was nil. There were properties for both sat_slider and hue_slider in the ViewController.h file, but something was still missing. I deleted the properties, re-dragged them in IB, and then I was able to set the slider position in viewDidLoad with no problems.
Check if sat_slider nil. Also make sure that it is properly connected in IB. If not, remove it and add it again in nib/storyboard. That should fix the issue.

Resources