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.
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 building a Safari App Extension using XCode 8.3 and Swift 3, following the Safari App Extension Programming Guide. The extension includes a popover that appears when the extension's toolbar item is clicked. The popover view contains a few buttons linked to actions the user can perform.
I want clicking one of these buttons to close the popover after its action has been performed. By default, clicking anywhere outside of a popover closes it, but I haven't been able to find any other way to close the popover, either in the guide or in the docs.
I know that NSPopover has a performClose method, but there doesn't appear to be a way to access the popover itself from within the extension: the app extension only lets you provide a SFSafariExtensionViewController, whose contents magically appear within the popover.
I've also tried using dismissViewController as described in this StackOverflow answer, but in my view controller self.presenting is always nil, and self.dismissViewController(self) just crashes the extension with the message:
dismissViewController:: Error: maybe this view controller was not presented?.
Lastly, I noticed a related question about programmatically opening the toolbar item popover has gone unanswered the past 6 months. This leads me to suspect Apple may simply have strict limits on how the popover can be opened and closed. Even if this is the case, it would be nice to know for sure what the limitations are.
I'll add an answer in case anyone stumbles upon this question.
A dissmissPopover() instance method has been added to the SFSafariExtensionViewController class. This can be used to programatically close the popover.
The default template given when creating a Safari App Extension in XCode gives you a SafariExtensionViewController class that extends SFSafariExtensionViewController and holds a shared instance as a static field called 'shared', so you can call the dismissPopover() method from that instance.
For example:
class SafariExtensionHandler: SFSafariExtensionHandler {
func myFunc() {
// do stuff;
SafariExtensionViewController.shared.dismissPopover()
// do other stuff;
}
}
I did it by calling dismiss method like below
#IBAction func onLoginBtnClicked (_ sender: Any) {
NSLog("Button clicked")
self.dismiss(self)
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I have been trying to learn how delegation with protocols work. I understood everything, but I can't think of when to use delegation other than when using table views and possibly scroll views.
In general, when is delegation used?
What is Delegation?
First of all, you should know that Delegation Pattern is not exclusive for iOS world:
In software engineering, the delegation pattern is a design pattern in
object-oriented programming that allows object composition to achieve
the same code reuse as inheritance.
But working with delegation in the iOS world is so common, I assume that you can see many of classes that provide a delegation/datasource for giving the ability to provide properties or behaviors for the used instance. It is one of main mechanisms of how objects talk to each other in CocoaTouch.
Alternatives:
However, delegation is not the only way to let objects talk to each other in iOS, you might want to know that there are:
NotificationCenter.
KVO (Key-Value Observing).
Completion handlers/Callbacks (using closures).
Target-Action.
Remark: in case if you are interested in comparing between them, you might want to check the following articles:
Communication Patterns.
When to Use Delegation, Notification, or Observation in iOS.
Delegates vs Observers.
When to use Delegation?
So, the question is: "So why should I use delegation instead of those options?"
I will try to make it simple; I would suggest the use of delegation when you have one to one relationship between two objects. Just to make it clearer, the goal of talking a little bit about the NotificationCenter is to try to make sense when delegations are used:
NotificationCenter represents one to many relationship; Simply, it works as: posting (notifying) a notification on a specific event and observing (get notified about) this notification -- it could be observed anywhere else; Logically, that's what one to many relationship means. It is a representation of the Observer Pattern.
How to Apply Delegation?
For the purpose of simplifying, I would mention it as steps:
Knowing the requirements: Each delegate has its own rules, listed in the delegate protocol which is a set of method signatures that you should implement for conforming this delegation.
Conforming for the delegation: it is simply letting your class to be a delegate, by marking it. For instance: class ViewController: UIViewController, UITableViewDelegate {}.
Connecting the delegate object: Marking your class to be a delegate is not enough, you need to make sure that the object you want to be confirmed by your class to give the required job to your class.
Implementing the requirements: Finally, your class have to implement all required methods listed in the delegate protocol.
For Example
Does it sounds a little confusing? What about a real-world example?
Consider the following scenario:
Imagine that you are building an application related to playing audios. Some of the viewControllers should have a view of an audio player. In the simplest case, we assume that it should have a play/pause button and another button for, let's say, showing a playlist somehow, regardless of how it may look like.
So far so good, the audio player view has its separated UIView class and .xib file; it should be added as a subview in any desired viewController.
Now, how can you add functionality to both of the buttons for each viewController? You might think: "Simply, I will add an IBAction in the view class and that's it", at first look, it might sound ok, but after re-thinking a little bit, you will realize that it will not be applicable if you are trying to handle the event of tapping the button at the controller layer; To make it clear, what if each viewController implemented different functionality when tapping the buttons in the audio player view? For example: tapping the playlist in "A" viewController will display a tableView, but tapping it in the "B" viewController will display a picker.
Well, let's apply Delegation to this issue:
The "#" comments represents the steps of "How to Apply Delegation?" section.
Audio Player View:
// # 1: here is the protocol for creating the delegation
protocol AudioPlayerDelegate: class {
func playPauseDidTap()
func playlistDidTap()
}
class AudioPlayerView: UIView {
//MARK:- IBOutlets
#IBOutlet weak private var btnPlayPause: UIButton!
#IBOutlet weak private var btnPlaylist: UIButton!
// MARK:- Delegate
weak var delegate: AudioPlayerDelegate?
// IBActions
#IBAction private func playPauseTapped(_ sender: AnyObject) {
delegate?.playPauseDidTap()
}
#IBAction private func playlistTapped(_ sender: AnyObject) {
delegate?.playlistDidTap()
}
}
View Controller:
class ViewController: UIViewController {
var audioPlayer: AudioPlayerView?
// MARK:- Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
audioPlayer = AudioPlayerView()
// # 3: the "AudioPlayerView" instance delegate will implemented by my class "ViewController"
audioPlayer?.delegate = self
}
}
// # 2: "ViewController" will implement "AudioPlayerDelegate":
extension ViewController: AudioPlayerDelegate {
// # 4: "ViewController" implements "AudioPlayerDelegate" requirments:
func playPauseDidTap() {
print("play/pause tapped!!")
}
func playlistDidTap() {
// note that is should do a different behavior in each viewController...
print("list tapped!!")
}
}
Quick Tip:
As one of the most popular examples of using delegation is Passing Data Back between View Controllers.
Delegation is used when you want to pass some information or state of object A to another object B. Usually object B is the object that created object A.
I will list some situations where you would use delegation.
Yes you're right. table views and scroll views use delegates because they want to tell whoever is interested (usuall your view controller) that "someone selected a row!" or "someone scrolled the scroll view!". Not only do scroll views and table views use delegates, UITextField and UIDatePicker and a lot of other views use delegates too!
View Controllers also have delegates. For example, UIImagePickerController. The reason why is roughly the same as above - because the UIImagePickerController wants to tell you messages like "an image has been selected!". Another example would be UIPopoverControllerDelegate. This delegate tells you things like "the popover has been dismissed!"
Other classes that use delegates include CLLocationManager. This delegate tells you things like "the user's location has been detected" or "failed to detect the user's location".
You can use delegation in your code when a certain view controller of yours wants to send messages to other view controllers. If it is a settings view controller, it might send messages like "the font size setting has been changed!" and the view controller that cares about the font size setting changing will know and change the font size of a label or something.
Delegate Method to Selectionimages
Create baseClass And Insert the following code
Create Another class then insert code
Delegation in IOS world and mostly in MVC (Model View Controller)
is a way for the View to talk to the Controller and it's called "blind communication"
and delegation means to give the " leading stick " to another object ( doesn't really care who is taking over but usually the Controller) to control over components that the view can not control on it's own (remember it's only a view) or doesn't own
to make it more simple ....
the controller can talk to a view but the view can not talk to the controller without Delegation
In one of my Watch Extension's interface controllers I have several WKInterfacePicker elements, and I need to know when the user has selected a value. According to documentation, WKInterfaceController should be able to implement pickerDidSettle(_:) method that has the corresponding picker element as parameter. For some reason the method never gets called when I use the pickers. Here is the basic structure of my implementation:
override func pickerDidSettle(picker: WKInterfacePicker) {
// Code inside this block is not called
}
If I mark the function with an #IBAction attribute and connect them with the picker elements in interface builder, the instance method works. However, this apparently prevents me to assign picker actions that receive all the picker values through which the user is scrolling.
#IBAction
override func pickerDidSettle(picker: WKInterfacePicker) {
// This function gets called, but blocks other actions
}
My interface controller inherits from WKInterfaceController and conforms to two custom protocols. How should I implement the method?
Edit: The issue was related to a possible bug in WatchKit, where pickerDidSettle(_:) will not be called without an existing #IBAction connection to the controller. I assume it is a bug, because related instance methods pickerDidFocus(_:) and pickerDidResignFocus(_:) work independent of the connection.
Sometimes this issue occurs, when something gets 'out of sync' between Xcode and the Simulator.
Just close the Simulator and clean and rebuild your app (via 'Product/Clean Build Folder') to recreate the 'sync.
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.