Create custom action in a class for use in Interface Builder - swift

I want to create a custom action connection in a class, which should be visible in Interface Builder. For example - I add action / target properties to NSView class just like this:
weak open var object: AnyObject?
open var something: Selector?
The action is something and the target is object.
Now I want in Interface Builder to have 'Send Action' link / connection available for something and to be able to make connection to a #IBAction method to some class (for example the controller of the view), just like it can be done for a simple NSButton. Maybe this is not possible, or maybe I must add some keywords in front of the custom action / target pair, the same way we need to make a property #IBInspectable to appear in Attributes Inspector.
Any help is welcome ;-)

I don't think you can do that. You can add #IBInspectable properties to custom NSView subclasses, but there is no selector or action data type for properties. The only allowed types are Boolean, Number, String, Localized String, Point, Size, Rect, Range, Color, Image, Nil.
Interface Builder has hard-wired support for handling the target-action properties of controls.
(BTW you should probably add the Mac OS tag to your quesiton. Most Apple traffic on this board is iOS-related.
EDIT:
As somebody said in a comment, if you're creating an object that has a target/action, it should probably be an NSControl, not an NSView. Controls are the object family that handle target-actions, and there IS a mechanism in Interface Builder for adding target/actions to controls.

In iOS, there is one somewhat cheesy option -- you could have an outlet to a UIBarButtonItem, and someone could then drop a standalone UIBarButtonItem in their xib or storyboard. The UIBarButtonItem can then have the target/action, and your class could then pull the target/action from there in awakeFromNib and assign the values to your own class. That approach may be replicable in MacOS using NSToolbarItem, but I have not tried. It's a kludgy option really, as it requires extra xib objects and may not be too obvious what is going on, but it can work. Basically, the UIBarButtonItem / NSToolbarItem would only be acting as a container for the target/action values.
If you can't subclass NSControl, it would seem the preferred option is to have a delegate, probably. That is usually fine, unless one controller needs to be the delegate for multiple instances of your class, in which case the delegate methods get more tedious. Another option is to have your class have a block property, which is performed upon the action, but blocks must be assigned in code and not set up in Interface Builder.

Related

iOS Custom Control

I am building a custom control that looks like the one in the image below.
It will basically be a menu with a slider. The arrows will allow me to change the three days show on the slider track. Acoording to the day I select with the slider I want to change some views on the main screen (this menu will on the bottom of my page). So basically this is the only thing that I will "listen" in my main controller: if some day has been selected.
I have figured out all the code I will have to write, but I am not sure wether I should subclass UIControl or UIView. And if so, where should I write the code of my controller (changing the days, adding the drag effec, etc ) in the UIControl (UIView)? Or should I subclass UIViewController, and write there all the code there. (but if so, why should I subclass UIControl (UIView) in the first way).
So basically I want to know what extra files I need to create, besides the view interface of my custom control(which I did in the IB), where should I put the code (IBOutlets, IBAction methods) and how do I communicate with the main view controller (I set the main controller as a delegate of my custom control?).
Sorry for the long post.
Thanks
I recommend to subclass UIControl. Users of this control can do [yourControl addTarget:self action:#selector(someMethod:) forControlEvents:UIControlValueChanged]; to react to changed values. In your control, when you have determined that a new day has been selected, you call [self sendActionsForControlsEvents:UIControlValueChanged]; and voila, all interested classes will get informed.
Keep that control as self-contained as possible. This means, only give it as much logic as you need to, and nothing more. Think about how you use Apple provided UI elements: try to make yours as generic (if practical; use common sense here). In short: you should thrive to make this control generic enough that it could be useful to you in other projects or other places of your app.
The short answer is you should subclass UIControl and put all of the logic to draw the component and interact with the component there. UIControl inherits from UIView and adds target/action behavior. This way you can sendAction:to:forEvents: with UIControlEventValueChanged whenever the date changes.
You could alternatively implement a delegate protocol for when the user changes the selected date.
For example:
#protocol DateSliderDelegate <NSObject>
- (void)dateSlider:(id)slider dateDidChange:(NSDate *)date fromDate:(NSDate *)oldDate;
#end
You don't want to use a UIViewController since it's job is to manage higher-level views, like the screen that your widget is on. You'll use a view controller later when you are consuming your component and do things like set the date to display initially and listen for change events.

how do I ensure a custom UIView I have is repositioned properly after an orientation change of the iPhone

how do I ensure a custom UIView I have is repositioned properly after an orientation change of the iPhone?
Background
As a parent view I have a UIViewController with an XIB
I have a custom UIView which is used in this parent (inserted in the XIB via Interface Builder). It programmatically at init creates some UIImageView's and adds them via "self addSubview", and then positions them via "self addSubview:imageView1" type approach
So the questions are how do I ensure I get the correct layout of these as orientation changes. Specifically:
Where do I trigger any such redrawing of the custom UIView? (e.g. is there a method in the parent UIViewController I should be using to trigger from?)
When the trigger occurs how do I request the redraw of the custom UIView? Do I need a custom method I create myself like "relayoutCustomView"? or should I be able to use an existing method in the custom UIView, in which case do I need to make sure in the custom UIView that my layout code is in a particular method?
thanks
how's this for the answer - seems to be starting to work, not sure if it's the best way:
separate out the layout code in my custom view and put in a separate method and make this public
this method will be called from within the custom view "init" method upon setup
also now however create an "didRotateFromInterfaceOrientation" method in the parent, and from within here also call into the new custom view layout method mentioned in [1]
noted it had to be "didRotateFromInterfaceOrientation", as with the "didRotateFromInterfaceOrientation" method it didn't work as the self.bounds result hadn't yet changed
How does this sound? I didn't use the "setNeedsDisplay" anywhere...

iphone - Interface Builder not loading custom class?

I generally don't use Interface Builder (I hate it). But I am being forced to use it, because I was invited to a project where people are using it. I am trying to create a custom UISlider.
I have created a UISlider custom class, with my own images for the slider parts.
If I add a new object to my main code using
mySlider *one = [[mySlider alloc] initWithFrame:CGRectMake(0,0,60,30)];
I see the slider as I created, beautifully.
But if I use this class on interface builder, to change the appearance of a UISlider I create there, the slider continues to have the same appearance as before and when I run the app the interface shows the slider with the default appearance, not the one I designed.
I created the slider on IB, simply dragging a UISlider on the interface and changing its class to the one I've created. Is there something else that has to be done? Why is it not showing as defined on the custom class?
thanks
You need to implement the initWithCoder: initializer. initWithFrame: will not be called when a nib is loaded.

Apply an affine transformation to view in Interface Builder

Is it possible to apply an affine transformation to a view directly in Interface Builder? I know I can attach an outlet and assign it to the transform in code, but I'd like the convenience of visually tweaking the transform in Interface Builder.
Nope. Sorry.
There are several properties that would be nice to set in IB. Occasionally Apple adds them, and then IB warns you that it's "only supported in iPhone OS 3.0 and above" or similar (I think this is the case for contentEdgeInsets on some class or other).
There's a very small chance that you can edit the XML, guess the right property name, and set it to the right string (probably whatever NSStringFromCGAffineTransform returns), but I think if it was possible, Apple would've included it.
There's a slightly larger chance that you can edit the xib, add a custom property, and use a category to override -[UIView initWithCoder:] and load your custom property. You can also write an InterfaceBuilder plugin for a custom view class which supports setting the transform in IB...

can i add an ivar (or property) to an existing class in objective-c?

I'm wondering if it's possible to add an ivar to the UIButton class? A bit like categories but not for a method(s) but for ivars.
I am programmatically creating and displaying an array of UIButton's which I then all link up to a single action method using –addTarget:action:forControlEvents: for a touchup event.
Now, my receiver method needs to know which of all the buttons was pressed but using the "(id)sender" approach doesn't cut it because the only thing differentiating all the buttons is the image its displaying and there is no way to get to that (I need a string). The buttons are all in different places so I could do some math to convert the position data into an "id" but if I change the positioning of the buttons down the line, I will need to change the math as well and I don't like that.
Can I just subclass UIButton and change nothing except for adding a (NSUInteger)idCode property? Then when I create the buttons I set the idCode, and when the target-action mechanism fires the action method, I can just do sender.idCode. Is this the way to do it?
Is there a better standard/elegant way of implementing this kind of multiple target-action see-where-it-came-from behaviour?
P.S.: Is there a quick way to type the backtick on a Mac?
You could do it this way. But this is not necessary - every UIView (and subclasses which includes UIButton) has the tag property which is just what you want.
UIButton as well as all UIView subclasses already has an integer property for exactly that purpose: tag