Right way to create a customizable uiview - objective-c

this question is about "style", because i think this is a very common problem and i'm looking for an elegant solution.
I have created some "advanced" UIView and i try to make them very customizable.
Usually i create the UIView structure inside a custom init method, but i need to know the value of all customizable parameter inside init method so sometimes i need a very long init method like:
initWithFrame:color:font:verticalspace:verylonglist:
I tried to use delegate design pattern but i need also to pass delegate inside init method.
My actual best solution is to leave empty the init method and move everything about layout inside a "configure" method. everytime i chance a property like background color or font i will call this method and i will rebuild the view.
I think there is a best way to solve this problem...
I'd be curious to see the code of UITableView Class, because with that class you can pass a delegate outside init method.

Check out something like a UIButton or UILabel. They both have tons of configurable aspects, however to simply create an instance of one of those objects, they need very little information.
In general, provide init methods that allow the consumer of your class to specify the least amount of information for the class to work.
If you do want to give the consumer a way to initialize the class with a bunch of values, consider using some sort of initWithDictionary: method that takes an NSDictionary of parameters. This keeps your method names short and allows the user to customize an arbitrary number of settings for your class.
You could also consider providing a way for the consumer to request an instance with some standard set of values. UITableViewCell, for example, has an initWithStyle:reuseIdentifier: method. The important part is the style - UITableViewCell provides several default styles like UITableViewCellStyleDefault and UITableViewCellStyleSubtitle.

I don't know if it is the standard/best practices way but I use a dictionary in cases like this and pass that to an initWithDictionaryinitializer. Would be possible too to create a class method that returns a 'default settings' type dictionary which can then be customized (and delegate set), so that not every param needs to be specified whenever the class is used.

Related

What type of parameters should be in a custom initializer?

When adding a custom initializer to a subclass, are there any rules as to what kind of parameters it should take? As an example, is there a particular reason why UIView doesn't have the initializer below, and would there be any argument for not adding it to a custom subclass of UIView?
- (instancetype)initWithFrame:(CGRect)frame backgroundColor:(UIColor *)backgroundColor;
I haven't been able to find an answer to this on the web, so I hope that some of you can enlighten me.
You can add anything in there. But then you can add so many that it becomes ridiculous...
initWithFrame:backgroundColor:tintColor:alpha:hidden:userTouchEnabled:...
It really depends on what you're using the subclass for.
For instance, you might create a PlayingCardView subclass. This is going to have certain properties... suit, faceValue. Because every card MUST have a suit and face value it makes sense to put these into the init method.
So it would be sensible to create an initialiser...
- (instancetype)initWithSuit:(Suit *)suit faceValue:(FaceValue *)faceValue;
because every card has to have a suit and a face value.
Equally, you could have another property backImage to set the image on the "back" of the card. So you could have...
- (instancetype)initWithSuit:(Suit *)suit faceValue:(FaceValue *)faceValue backImage:(UIImage *)backImage;
Now you can use one of two initialisers. One for the "default" back image the other to set a custom back image.
Then you get into designated initialisers in this case, it would be preferable not to have repeated code in both init methods so you would make the first method like this...
- (instancetype)initWithSuit:(Suit *)suit faceValue:(FaceValue *)faceValue
{
UIImage *defaultBackImage = [UIImage imageNamed:#"defaultCardImage"];
return [self initWithSuit:suit faceValue:faceValue backImage:defaultBackImage];
}
And the designated initialiser would do all the actual setting up of the card.
In reality though, there is no fixed way and no correct way of doing this and it can take a while to find a pattern that you find easiest/best to use.
Your custom initializer can take any parameter you want to. As any other class, UIView define some let's say basic behaviour, so if you need any other behaviour you are free to add it using a subclass for example. Don't complain that UIView or any other classes don't have methods that you think would be good for them to have.
I would recomend you not write a custom initializer which has many arguments because it increase the complexity. Think exactly what is needed for that classes to be initalized correctly.

How does forwardingTargetForSelector: work?

I have a UIBarButtonItem. When it receives a message it cannot handle, I want it to forward that message to a particular view controller.
I thought I might be able to accomplish this using the bar button item's forwardingTargetForSelector: method, but apparently no such property is found on objects of type UIBarButtonItem. (Point of terminology: Does that mean forwardingTargetForSelector: is a private property? edit: Wait, I think I'm confused... methods with a colon at the end aren't properties... so can you ever make public a method (like a getter/setter) to which parameters are passed?)
And does that mean that in order to set the value of forwardingTargetForSelector: I must do it from within the .m file of the object for which I want to set it? Which would mean that I would have to subclass my UIBarButtonItem?
And if so, why is this not a public property of NSObjects?
And moreover, what's the best way to achieve my forwarding goal, preferably avoiding subclassing?
additional information:
It all stems from my inclination to reuse a single action in response to various instances of an identical button being pressed. The action is currently contained in my delegate (see How should I implement [almost] identical actions used throughout various VCs? (Answer: use a category)) and varies only in that it should send a presentViewController message to the view controller that instantiated the button that sent the action. Thus, in the action, I can send a presentViewController message to sender, which is an instance of the button, and I want to be able to forward that message to the view controller that created that instance of the button, which I can do if I set each button's forwarding property immediately after it is instantiated in its respective view controller.
I hoped to avoid the "why" just to make the question shorter, but there ya go.
forwardingTargetForSelector: is not really a property; it's more like a question the runtime asks an instance when the instance doesn't respond to a message.
It can't be a property in the #property/declared-property sense, because each selector could have a different target; there would need to be a mapping behind it. That's just not how declared properties work.
UIBarButtonItem descends from NSObject, and it inherits this method along with all the others, but you can't "set" the forwarding target for a selector from outside an instance (without creating some other machinery to allow you to do so, anyways -- possible, but not available by default).
In order to utilize this method, yes, you have to implement it in the class that is doing the forwarding. This does indeed mean subclassing. It also means that the forwarding instance needs to have a reference to the object to which it is forwarding; this requires careful design.
forwardingTargetForSelector: is all but certainly not the correct way to achieve whatever your goal is. In general, in fact, it's a bit esoteric.
I'm not sure exactly what problem you're trying to solve ("making a button forward messages it doesn't respond to" is still rather general -- in particular, why is that necessary?), so it's hard to be more precise.

Passing Data To Delegate

I have a bunch of outlets in a controller object that another controller needs to be passed. If it was only 2 or 3 values, I'd just pass them as parameters to the delegate method (not directly the outlets, but by copying the value to variables)
However, there are quite a few. What is the best way to handle this? I see three approaches:
I could create a new object that holds all these properties and pass that.
I could just pass the controller in the delegate method [self.delegate didClickDone:self]. The problem with this approach is this: am I allowed to access another controller's outlets from the outside?
I could follow the 2nd option but copy every outlet's value to a property and allow the other controller to access them via accessor methods.
What is the best way to approach this?
You are always allowed to do what you allow yourself. However some approaches may prevent Apple from accepting your App for the Appstore. This is not the case here ;)
If there are many values to pass I'd go for
An array if the passed objects contained are of the same type/kind
Use a data class if the values are heterogeneous in nature. Like in Refactoring by M.Fowler -> Introduce Parameter Object (page 295).
The dirty way would be, as you suggested, to open the Outlets to other instances than the view controller itself. Prevent that nosy behaviour of other classes.

Using Protocols in Objective C to Transfer Data Between Different Objects?

Hey guys, I currently have a root table view which has a toolbar at the bottom and has labels and a refresh button within it, much like the Mail app's toolbar. This root table view controller obtains data from a server by allocating and initializing a DataUpdater class. Within this class are the NSURLConnection delegate methods that are called while communicating with the server.
As you can probably guess, I need to know when certain (delegate) functions are called within the DataUpdater class and the values of the parameters passed to these delegate functions so that I can update the labels on the toolbar accordingly (i.e. Connecting..., Updated, etc).
The problem I am having is determining how to notify the root table view controller of what is going on in these delegate methods. Would I use protocols, if so how? I have been skimming the documentation and don't quite see how I would get this effect. Or would you suggest I implement my program another way?
Thanks in advance!
A protocol is a kind of contract that says: I promise to provide the non-optional methods defined in the protocol, and maybe even the optional ones. It's purpose is like Java interfaces: to work around missing multiple-inheritence.
The delegate pattern in Objective-C normally works like this: you define a protocol, and then in your class, you define a variable like id<MyProtocol> myDelegate; and define a setter and maybe getter (either via normal methods, e.g. - (void)setDelegate:(id<MyProtocol>)aDelegate; or via properties.
Note that the delegate is not retained ! So if you work with a property, you need the assign option, not retain.
Now back in your class, you check whether myDelegate is nil and if not, you can directly call its non-optional methods. If you want to call an optional method, you first need to verify its presence via respondsToSelector:.
So if you decide to use the delegate pattern, you need to define a protocol, add that protocol to your root table view controller, implement the necessary methods there, and make sure to call [foo setDelegate:self]; or something similar to inform your other class that the root table view controller is the delegate. And of course implement the delegate calls in your class.
Edit:
An alternative might be to use NSNotifications, BTW. The advantage of notifications is that you can have multiple objects listen and react to them. The disadvantage is that you cannot (directly) pass values back. For example, you can define a delegate method that asks the delegate whether to do something or not. That's not possible with notifications, it's more like shouting into a room instead of having a one-to-one conversation.
DarkDust's answer about protocols is fine but I would like to add some things to it.
One underlying thing that is often forgotten when it comes to delegation is object ownership. When a program is running it creates a tree of objects. Its root object is the application delegate and for example it owns a navigation controller, which owns the individual view controllers, which own the view and the view owns its subviews and so on.
Often the question comes up: "Why is the delegate not retained, just assigned?" The problem is that if you send a message to a deallocated object the program crashes. So how do you make sure the delegate stays around? The answer is object ownership.
I give you an example: a UITableView and its data source which is the TableViewController which is nothing but a delegate. The TableViewController holds a reference with its view property to the UITableView, so it owns the TableView. That means when the tableView is alive there must also be its parent object present, which is the UITableView's delegate. So there is no danger that the delegate goes away somehow.
In the end it is again all about memory management.
Take home message is: think upfront about object ownership will make your program mode modular, easier to maintain and will lead to a looser coupling between individual objects.

Can you add a property at run-time when coding with Objective-C

I was wondering if it is possible at run-time to dynamically add new properties to an Objective-C object instance?
My initial thought would just to overrride the getValueForKey to "fake" a property but it seems like this doesn't work with CoreAnimation. What I want to achieve is to be able to animate custom properties. I have been able to get that to work if I create a subclass of CALayer and add declared properties to my subclass. If I try to use the getValueForKey/setValueForKey strategy it seems like CoreAnimation doesn't care for that and it is explicitly looking for declared properties.
I would like to be able to dynamically add the properties because I might not know what property I want to animate until runtime. I can of course create a CALayer subclass that has all the properties that I would ever want to animate...but just wondering if there is a nicer way to do this...
Thanks,
Peter
Have you tried overriding valueForUndefinedKey: instead? (I do this on a custom NSObject subclass that can have various properties whose names are pulled from a database.)
You could override -respondsToSelector: and -doesNotUnderstand: to process incoming messages dynamically if need be.