IBAction from (not to) my custom class - ios

Remember typical situation: we have UIButton, we have some event triggered by something (in our case let it be UITouchUpInside) AND some IBAction somewhere (for example in our UIViewController) that will be called when event occurs. IBAction is created by control-dragging from my button inside my view controller.
Now image another situation: I have some custom class, some event occurs inside it AND I want some IBAction to be performed when my custom event in my custom class occurs. To be more specific, I want to connect object to my view controller in storyboard, then control-drag from one of my events from created object to my view controller's IBAction (exactly like I did for UIButton and UITouchUpInside). In other words probably I need some kind of selector (?) to be set up from storyboard to call later.
The whole point of all it is to quickly drag from events in my custom class into another place to create (and later call) IBAction, so please do not mention delegation of any form.

Related

How do I pass the UIButton pressed from my custom UIControl to my view controller?

I have a Custom UIControl with 3 UIButtons:
This UIControl is embeded in a UIView (the Yellow Rectangle) inside a view controller:
Every button has a different tag (tag1, tag2, tag3). I need to identify which button is pressed in the view controller.
I'm trying to use sendActionsForControlEvents in my UIControl to send it to the view controller.
[self.button1 sendActionsForControlEvents:UIControlEventTouchUpInside];
[self.button2 sendActionsForControlEvents:UIControlEventTouchUpInside];
[self.button3 sendActionsForControlEvents:UIControlEventTouchUpInside];
I don't know how to receive it in my view controller and how to identify which button is pressed.
Can anyone help me?
Thanks.
I solved creating #property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *tabsCollection; in my UIControl.
Then in my ViewController I create a loop to iterate this IBOutletCollection and adding an action on each button to control the touch event.
for (button in self.customTabsComponent.tabsCollection) {
[button addTarget:self
action:#selector(tabPressed:)
forControlEvents:UIControlEventTouchUpInside];
}
Firstly, define the responsibilities.
If you develop a custom control that manages a collection of buttons it should be responsible for receiving the actions in those buttons and forwarding them to the view controller via its own interface.
Your view controller should have no responsibility of managing those buttons at all.
Otherwise, if the responsibility of managing the buttons, hence the code, is inside your view controller, then your custom control is not self-contained, not a reusable control. You simply won't be able to use it with another view controller without copy-pasting that code inside your view controller that makes the button work.
Now, if you custom control is a subclass of UIControl you cannot use sendActionsForControlEvents to pass information which button was tapped, since this interface is designed to have one action, but for different event types. That you need is different actions, or some kind of additional information that indicated which buttons was tapped.
Look at UIAlertController for example. One of the ways would be to implement a dynamic array of actions, which would be created in your view controller, passed to your custom control and invoked by the buttons. Your buttons would need to allocated dynamically inside your custom control. A bit more work, but this would be the most appropriate approach.
If you want to have a static set of buttons, you can add properties of block type to your custom control. Different buttons would invoke different blocks.
Alternatively, you can make a new delegate protocol and pass the index of pressed button, see UIAlertViewDelegate. This is a bit old school approach, which was used before blocks became available.

How to tell an UIView about an event in a collectionviewcell?

Reference Image
Hello!
I'm making a Swift project using the MVC pattern, and I'm hoping you guys could help me with how to best pass information between different classes.
I'll do my best to describe, but please take a look at my badly drawn reference for some additional clarity.
The project is build with a Model, that's a simple class with the rules of the project, a View Controller, that has an instance of both the View and the Model, passes information between them and initiates the View.
The View has all the element variables, and sets the layout. The view has a CollectionViewController, that in turn has the CollectionViewCells. One of those cell has a Button. There's of course some other classes, but they're not relevant.
What I'm trying to accomplish is that I want the user to click the Button in the CollectionViewCell. The CollectionViewCell should then tell the View that the Button was pressed, and the View should take some Information and send to the ViewController, that will interpret it for the Model.
More or less Button > User Clicks -> (CollectionViewController) -> View > View collects Information -> CollectionView -> Model.
I cannot delegate the buttonclick event to the View, as UIViews cannot be delegated to. I can use a Closure to send the buttonclick event to the CollectionViewController, but I don't seem to be able to send it further from there.
So the two alternatives I'm looking at is creating a NotificationCenter just to tell the View a button has been pressed, or making the View a ViewController too. Neither seems ideal. Does anyone have a better solution, or any insight on what I should do here?
One solution based on your reference image can be this:
you can send your click event with clouser to your collectionView parent view and in your view have a weak instance of your parent viewController. when the clouser called, you can call the method from your parent instance and update your model

Listening for input in programmatically instantiated xib templates

I am currently messing around with programmatically adding and removing template Views; to remove a UIView template, a user has to tap a button within it.
However, I am not sure how to get the parent view controller to handle button inputs. I would like the parent view controller to know when the button is pressed instead of having the xib class handle that because otherwise I would have to a lot of circling and it would get annoying really fast.
Likewise, I cannot manually connect the button itself to the ViewController class because these xibs are added in during runtime.
How do I get a parent ViewController to handle the UIButton inputs within a custom XIB template?
Likewise, I cannot manually connect the button itself to the ViewController class because these xibs are added in during runtime
That's not true, because you can certainly call addTarget(_:action:for:) to connect the button action to the view controller target at the time you "add the xib."
However, an even cooler solution would be configure your button in the xib with a nil-targeted action. This means we don't need an explicit target; we simply have to know the name of the method that the action will call. The view controller is higher up the responder chain than the button (once you "add the xib" to view controller's view), and so when the button is tapped, the method will be found dynamically by walking up the responder chain.
you can use the method
button.addTarget(self, action: #selector(yourMethodName), for: .touchUpInside)
here the "button" will be the reference of the button in your xib. "self" will be the reference of the viewController in which you are adding the .xib.

UITapGestureRecognizer not (always) working for targets that are different than UIViewController

I have a custom iOS controller class that adds a UIButton programatically to a given UIView. The button has a UITapGestureRecognizer associated to it but it works only when the custom controller is a #property of the UIViewController that embeds the view and button.
See https://github.com/vasile/ios-gesture-recognizer/ with 2 button examples, the 1st one doesn't record the tap gestures.
Can someone explain why ?
It's all about object lifetimes. In order to be the target of the button when it is tapped, the target object must exist at the time the button is tapped.
That's not true for the first button (Nothing Will Happen), because the target is just a local variable that immediately goes out of existence. The other target objects, on the other hand, are persistent. A property of a view controller instance, for example, persists as long as the view controller does. And a view controller is usually very long-lived; in particular, if properly used, it lives as long as the button does if the button is a subview of the view controller's own view!

MVC: should i add and implement touch gestures in the View Controller or my custom subclass of UIView

I have this custom subclass of UIView, called productCardView, which is pretty simple and has some UIImageViews and UILabels as it's subviews. I add the subviews and set them up the - (void)drawRect:(CGRect)rect method and everything sits nicely.
In my view controller I get some data from a remote server and so populate the productCardViews that should be seen. the purpose is when the user taps on each of these cards the program goes to a target url(a NSURL property of each card).
THE QUESTION IS according to basics of MVC, should I add a UITapGestureRecognizer to the view, inside my implementation of the productCardView or in my view controller?
If I am to add it in the view controller, Basically I would put the appropriate code in the viewDidLoad method, where I create instances of cards, but in case I should implement it in the View itself, where should I put the code? (in -(void)drawRect:(CGRect)rect?)
Whether the tap is on the card or on the view controller you should be loading the url from the view controller.
So, that means either...
Card feels the tap and calls a function in its delegate (the view controller) that then loads a URL.
or
Tap gesture recogniser (in the view controller) gets the tap from the card and open the URL.
If the card is a control used in several places (or if you have several on the screen) you might be better making the productCardView a subclass of UIControl instead of UIView. (UIButton, UISlider, etc... are all subclasses of UIControl).
There isn't much you need to change but you can do something like...
[productCardView addTarget:self action:#selector(cardTapped:) event:UITouchUpInside];
just like a button.
You then handle the touch in the card view and trigger the action for the event UITouchUpInside.

Resources