I have the following method
#IBAction func clearDisplay(sender: UIButton)
and I want to overload this method so that I can clear the display if some conditions are satisfied in my code. So
func clearDisplay()
My idea is to simply simulate pressing the clearDisplay button but am not sure how to do that. Does this sound like a good idea? How would I simulate pressing the button?
As Matt says, just call the IBAction method directly. You should probably change the object type of sender to AnyObject, and then use
clearDisplay(self)
Which will pass in the view controller as the sender.
In your clearDisplay method, if you have code that does something with the button, check it's type first to make sure it really is a button.
Related
I've recently come across the technique of swizzling. It looks like it's not a super common practice to use and I can understand the drawbacks I keep seeing listed for it. But as it's a thing that we can use in swift, I was hoping I could throw together something in a playground or practice project.
The idea I had was to use swizzling for analytics purposes. At this point, just to print off what the user is actually doing. I can't really think of interactions the user might make besides tapping buttons on their screen. I figure, I might be able to swizzle UITapGestureRecognizer and make it run a function tracking tap position, etc. But that's too much information for me. I just want to know when a button is tapped. So I think the only thing I need to swizzle is the UIButton. But we set the selector for UIButtons or we set them up as IBActions.
So my question is, Is there a way to get a generic call to any button's method selector? That way, I can swizzle the button in one place. Make it run the analytics and then call the primary function all without actually changing my code in my UIViewController classes directly.
I would post my attempts, but their rather sensical and I couldn't get anything to even run.
Is this a possibility?
You can try swizzling sendAction(_:to:for:) in UIControl. UIControl calls this whenever an event happens for every target-action pair.
extension UIControl {
// call this at an early point in your app lifecycle
static func swizzle() {
if let originalMethod = class_getInstanceMethod(UIControl.self, #selector(sendAction(_:to:for:))),
let swizzledMethod = class_getInstanceMethod(UIControl.self, #selector(swizzled_sendAction)) {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
#objc func swizzled_sendAction(_ action: Selector,
to target: Any?,
for event: UIEvent?) {
swizzled_sendAction(action, to: target, for: event)
if self is UIButton {
// do some analytics things
}
}
}
Caveats:
Since this method is called for every target-action pair, this means that this only works if all your buttons have exactly one target-action pair, and only for the event touchUpInside (a "tap"). In other words, for each button, you only call addTarget once, or add one connection in the storyboard for touchUpInside.
If a button does not have any target-action pairs, then tapping it does nothing. The swizzled method will not be called.
If a button has multiple target-action pairs for touchUpInside, then tapping it would cause the swizzled method to be called multiple times.
If a button has target-action pairs for events other than touchUpInside, then triggering those events would also cause the swizzled method to be called, though this can be prevented to an extent if you carefully check the UIEvent parameter.
To be honest, I would rather just subclass UIButton, instead of swizzling. Make a subclass that does something like this upon its creation:
addTarget(self, action: #selector(doAnalytics), for: .touchUpInside)
...
#objc func doAnalytics() {
// ...
}
I'm developing a custom class in Swift based on NSObject. It's a statusMenu icon/menu helper. When I receive an event for the icon being clicked in my custom class, I want to pass this on in the same way an NSButton allows to create an IBAction to respond to the user clicking the button.
How do I do this?
Code:
I'm registering a selector in my class to listen to clicks:
statusItem.action = #selector(statusBarIconClicked)
The selector receiving this:
#objc func statusBarIconClicked(sender: AnyObject) {
print("clicked clicked!!")
// pass sent action on through a new sent action... how?
}
I want this to be linkable to the user in the same way a button can lead to this:
#IBAction func myClassSaysMenuWasClicked(_ sender: Any) {
// Reacting to that
}
Googled for a good while and found: nothing.
I take it that you're asking about this sort of thing, displayed in the Connections inspector (this is iOS, not macOS, but it's the same idea):
The question would then be: when the user selects an instance of my class in the nib editor in Xcode, I'd like those Sent Events to appear in the Connections inspector so that the user can hook up one of them and use the target-action architecture.
You can do this only if your class is a Control subclass. Thus, for example, in iOS, a custom UIControl subclass displays those sent events in Interface Builder.
If you can't do that, then the programmer won't be able to configure your target-action in Interface Builder. You can certainly implement a target–action architecture, but the programmer will have to set the target and action in code. (You could do half a job of it by making the target an outlet, of course.)
I worked around the comment above and googled further. I found the solution being to change from NSObject to NSController in this line:
class StatusMenuController: NSControl, NSMenuDelegate {
And run this command when I want to trigger the sent action:
if let theAction = self.action { NSApp.sendAction(theAction, to: self.target, from: self) }
The if-command of course checking so that an action is actually set before trying to use it.
I found no ways during my research to add any further sent actions. The way to go here seems to be delegates.
I am trying to make a reset button for my app that will reset the UI to the original state. I have made a UIButton and linked it to the ViewController, but I have no idea where to go from here. I tried using the following code:
#IBAction func resetToOriginalState(sender: UIButton) {
self.resetToOriginalState (sender: UIButton)
}
It gave me the following error:
Editor placeholder in source file
Sorry if there may be an obvious answer, but I am very new to Swift and Xcode.
Is there any other way to create a reset button?
The error:
Editor placeholder in source file
Is because you are calling a function with the UIButton Class name instead of the actual button.
#IBAction func resetToOriginalState(sender: UIButton {
// this line is wrong, you shouldn't have UIButton in here
self.resetToOriginalState (sender: UIButton)
// the line should read
self.resetToOriginalState (sender: sender)
}
This way, you are passing the actual button into the function that was passed to resetToOriginalState
Seems you have too IBAction for the same button, check how many time you have #IBAction func resetToOriginalState(sender: UIButton) in your code and remove the references from the references Interface list to clean it, should there be only one :
It depends what is in the scene and what do you need to reload. As far is I know you can't really segue a ViewController to itself, but here are few options:
Try to add loadView() when the button is pressed
Duplicate the view controller, and segue between the two. (might be risky and create more work)
Reset your variables to their initial state when the button is pressed
You should give us more detail because this is implementation specific.
Nevertheless, it's not very clean, but depending on the architecture of your code, you might be able to generate a new instance of your view controller, destroy the current one, and present the new one.
In my interface file I said I conform to the UIAlertViewProtocol and I implemented the alertView:clickedButtonAtIndex: method in my implementation file, and normally whenever the alertview button is pressed (the button that makes the alert go away)that method gets called. Well, it gets called most of the time, but for one of my alert views it doesn't get called after I press the cancel button on it, what would be a reason for this?
Oh Its because in one of my alert views for the delegate parameter i passed in nil instead of self, thats embarassing.
I got only so far to be able to switch a view by pressing a button or with a timer. But I don't get it to switch a view by events like didReceiveLocalNotification. Is it impossible to do this?
Yes, that's possible , Because usually we specify the method with notification in which we wish to receive it.
Put the same code from your UIButton action: to notification method.
Yes, all you have to do is doing the switch inside the method didReceiveLocalNotification.
You will need to send a message to the view controller you want to perform the switch.