Detect a double click on a NSTextView image - swift

I'm trying to detect when a user double click on a image in my NSTextView to open it in preview for him to see it in a bigger from.
I currently didn't find any information on how to get the event of the double click in the NSTextView, NSLayoutManager or NSImage.

In order to detect a double-click event on an image in an NSTextView, you can use the NSTextViewDelegate protocol to receive notifications when the user interacts with the text view.
You can set the text view's delegate to an object that conforms to the NSTextViewDelegate protocol, and then implement the textView(_:clickedOn:at:) method to detect when the user clicks on an image.
class MyTextViewDelegate: NSObject, NSTextViewDelegate {
func textView(_ textView: NSTextView, clickedOn cell: NSTextAttachmentCellProtocol, at charIndex: Int) {
if let attachment = cell.attachment, attachment is NSImage {
// Handle double click on image
}
}
}
Then in your view controller you can set the delegate
textView.delegate = MyTextViewDelegate()
You can track the number of clicks by using a variable to track the last time the function was called, and comparing the current time with the previous time. If the time difference is less than a certain threshold, you can consider it a double-click.

Related

Dismiss delegate for UIMenuElement Swift

I have a UIMenu on my app and I want to detect when a user taps outside(dismiss) the UIMenu. But it seems like Apple does not support any delegates by default for this action.
Because when a user taps outside I want to change the image of the button.
Sample Image
One rather hacky way I found, is to subclass UIButton and override contextMenuInteractionWillEndFor.
class MyButton: UIButton {
override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willEndFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
super.contextMenuInteraction(interaction, willEndFor: configuration, animator: animator)
print("ending!")
}
}
contextMenuInteractionWillEndFor is the delegate method that is called when you dismiss a UIMenu, but if you are setting the button's menu property, the button itself will become the delegate of the UIContextMenuInteraction, and you can't set it to something else, which is why you have to subclass UIButton.
Compare this to when you are adding a context menu using addInteraction, where you have control over the UIContextMenuInteractionDelegate.

Is it possible use different instance of UIMenuController for UITextField in Swift?

According to link we should use a singleton UIMenuController instance which is referred to the editing menu.
The problem is I want to show extra items in different situations. For instance, I want to just show "copy" item when keyboard is up. and show "copy" and "reply" when tapping on a tableview row.
When I add "reply" to the UIMenuController instance it is shown when tapping on UITextField too. therefore, I added these codes:
func textViewDidBeginEditing(_ textView: UITextView) {
var nonReplyMenuItems: [UIMenuItem] = []
if let allMenuItems = UIMenuController.shared.menuItems {
for menuItem in allMenuItems {
if menuItem.title != "reply".localized {
nonReplyMenuItems.append(menuItem)
}
}
}
UIMenuController.shared.menuItems = nonReplyMenuItems
UIMenuController.shared.setMenuVisible(true, animated: true)
}
It fixed the problem in most situations, but not all.
when keyboard is up and tapping on a row in tableview "reply" will be added. Then when I tap on UITextView the reply will be shown there too.
It seems your scenario is like it:
tap on textfield ----> shows copy
tap on tableview ---> shows copy and reply
tab on textfield ----> shows copy and reply (you want only copy shows)
As I know the textViewDidBeginEditing calls when your text filed is not editing and you tap on that; So if you have two textfileds by switching on that method calls every time but when you are switching between a text filed and another action base object your text field is editing and its state has not changed.
When you touch on tableview you must call textfield.resignFirstResponder() so when you tap on text field again the textViewDidBeginEditing calls again, the problem of this is hiding keyboard; The better way I preferÙˆ is adding function to touch down of text field or on gesture to do what you write on textViewDidBeginEditing method

Detect maximize/minimize window event and do it programmatically

How detect Minimize/Maximize window and do this programmatically. I know that i need use NSWindowdelegate, but:
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowdelegate {
func windowWillMiniaturize(_ notification: Notification) {
print("1")
}
}
This not working. And how to maximize/minimize programmatically have no idea.
It is all in Apples documentation on NSWindowDelegate: https://developer.apple.com/reference/appkit/nswindowdelegate
You can implement in you class that is set as delegate for your window:
func windowWillMiniaturize(Notification)
Tells the delegate that the window is about to be minimized.
func windowDidMiniaturize(Notification)
Tells the delegate that the window has been minimized.
func windowDidDeminiaturize(Notification)
Tells the delegate that the window has been deminimized.
NSWindow has methods - easily to find when visiting the documentation: https://developer.apple.com/reference/appkit/nswindow
You can call from anywhere on your window:
func performMiniaturize(Any?)
Simulates the user clicking the minimize button by momentarily highlighting the button, then minimizing the window.
func miniaturize(Any?)
Removes the window from the screen list and displays the minimized window in the Dock.
func deminiaturize(Any?)
De-minimizes the window.

Get reference to current object

My goal is to safe a reference from the button, label or textfield inside a variable.
The problem is that I don't know on which control the user tapped.
I am having a simple application which looks like this:
The user can touch any control.
It is easy enough with just those three controls because I can drag in a action. But if I am having many of them I can't handle them all over the action methods. Is there a general way in which I can safe a reference to the control in a variable so that I can know which of the controls is the active one?
Edit
As suggested I am using a function and assigning the variable to the sender of the function. This is how it looks in code:
var currentObject: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
myTextfield.action = #selector(myAction)
}
func myAction(sender: NSTextField)
{
print("aktuell: \(sender)")
currentObject = sender
}
As you can see this only works for a NSTextfield. Is there a way in which the function works for every control?
Set the tag attribute for each item, and then you can check sender.tag to identify which object is calling it.
To set the tag, select the Attributes inspector in Storyboard (upper right side - middle button of Utilities) and look for this section:

Move a label left from its position in swift

I have put a label in a text box and I want to shift this label to left side after tapping this text box. In the following screenshot 'hello' is a label and I want to shift it left after tapping on the text box.
I wrote the following code but the 'textFieldDidBeginEditing' function is not called
#IBOutlet weak var lblhello: UILabel!
func textFieldDidBeginEditing(numtxt: UITextField!) { //delegate method
lblhello.frame = CGRectMake(98, 156, 42, 21)
}
I'm making the assumption based on previous comments that you want to manually specify the frame size, and aren't using constraints.
First off, you need to add UITextFieldDelegate to your viewController.
This is done like so in the beginning of your VC class:
class yourViewControllerName: UIViewController, UITextFieldDelegate {
Then you want to specify the delegate of your UITextField, (most likely in viewDidLoad)
yourTextFieldName.delegate = self
Now you can use the textFieldDelegate functions. The on you're looking for is probably this:
func textFieldDidBeginEditing(textField: UITextField) -> Bool {
lblhello.frame = CGRectMake(160, 200, 60, 40)
return true
}
If everything is set up correctly that should change the frame of lblhello when a user begins editing your UITextField.
One common problem I often hear for people saying this doesn't work. Is caused by the label being specified in viewDidLoad(). Most of the time you want to declare the label before viewDidLoad(), otherwise functions like this doesn't have access to it.
In case you want to move it back to it's original position afterwards. You do almost the same as you did in textFieldDidBeginEditing except you use textFieldDidEndEditing instead.
However, on a side-note. I do suggest getting used to using constraints rather than setting the frame for things like this.