swift unhide buttons in one column if mouse is over row - swift

I working with swift 4 for osx.
I have a view based NSTableView with 4 columns.
the cells in each column has got the same custom cell class:
class CustomCell: NSTableCellView {
#IBOutlet weak var btnInfo: NSButton!
private var trackingArea: NSTrackingArea!
override func awakeFromNib() {
super.awakeFromNib()
self.trackingArea = NSTrackingArea(
rect: bounds,
options: [.activeAlways, .mouseEnteredAndExited],
owner: self,
userInfo: nil
)
addTrackingArea(trackingArea)
}
override func mouseEntered(with event: NSEvent) {
super.mouseEntered(with: event)
btnInfo.isHidden = false
}
override func mouseExited(with event: NSEvent) {
super.mouseExited(with: event)
btnInfo.isHidden = true
}
}
Now i would like to realize the following situation:
if the user goes with the mouse over a row, the btnInfo should be visible and hide again, it the mouse leaves the row.
problem is (with the code above), that my apps crashes, because btnInfo will be nil
Logically: Because this button is only in column 4 available.
in all other columns it will be nil.
how can i solve this?

The solution is to add an NSTrackingArea to the entire view, not the individual cells. Then on the entire table view, you can get the mouse move events, take the NSEvent's locationInWindow. Then NSTableView has a method row(at point: NSPoint) -> Int that can get you the current row that should be highlighting the button.

Related

How to hide the cursor in Swift?

I'm trying to make a first person game where moving the mouse changes the camera angle, similar to in Roblox in first person mode or in Minecraft. How to do that? I'm asking how to do basic functionality so there is no code to provide. I just need to know how to hide and lock the cursor position in Swift or more specifically Metal or Cocoa.
You can subclass NSView, add a tracking area for mouse entered and exited, override NSView mouseEntered and mouseExited methods and just hide and unhide the cursor there:
import Cocoa
class HiddenCursorView: NSView {
override func awakeFromNib() {
addTrackingArea(NSTrackingArea(rect: bounds, options: [.activeAlways, .mouseEnteredAndExited], owner: self, userInfo: nil))
}
override func mouseEntered(with event: NSEvent) {
NSCursor.hide()
}
#objc override func mouseExited(with event: NSEvent) {
NSCursor.unhide()
}
}

How to detect which NSTextField is clicked in Cocoa?

I have two NSTextField objects which I want to highlight when user clicks on it.
The initial text field is already highlighted on NSWindow load. I am able to get mouse down event for text field click, but unable to distinguish which textfield did the user tapped.
I tried using hitTest on the text field using the NSPoint obtained from the NSEvent object, but the NSView returned is nil. The view it returns is that of the window's view and not that text field.
class SettingsViewController: NSViewController {
private var sview: SettingsView?
override func viewDidLoad() {
initEvents()
}
override func loadView() {
if let settingsView = SettingsView.createFromNib() {
self.view = settingsView
self.sview = settingsView as? SettingsView
}
}
func initEvents() {
self.sview!.emailTextField.delegate = self
}
}
extension SettingsViewController: NSTextFieldDelegate, NSTextDelegate {
override func mouseDown(with event: NSEvent) {
self.log.debug("mouse down: \(event.buttonNumber), \(event.eventNumber), \(event.locationInWindow)")
// How to know which text field triggered this?
}
func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
self.log.debug("control delegate")
return false
}
func textField(_ textField: NSTextField, textView: NSTextView, shouldSelectCandidateAt index: Int) -> Bool {
self.log.debug("text field should select")
return true
}
func textShouldBeginEditing(_ textObject: NSText) -> Bool {
self.log.debug("text field should being editing")
return true
}
}
class SettingsView: NSView {
private let log = Logger()
private static var topLevelObjects: NSArray?
#IBOutlet weak var emailTextField: ASTextField!
#IBOutlet weak var passwordTextField: NSSecureTextField!
// ...
}
I am adding delegate to only one text field.
self.sview!.emailTextField.delegate = self
But when I click on the passwordTextField, I am getting the mouse click event as well. Why is this happening?
How to distinguish NSTextField mouse click and highlight the text field?
I tried subclassing NSTextField and adding click handler, but it is not working.
class ASTextField: NSTextField {
private let log = Logger()
required init?(coder: NSCoder) {
super.init(coder: coder)
bootstrap()
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
bootstrap()
}
override func awakeFromNib() {
super.awakeFromNib()
bootstrap()
}
func bootstrap() {
self.delegate = self
}
}
extension ASTextField: NSTextFieldDelegate {
override func mouseDown(with event: NSEvent) {
// This is not working
self.log.debug("mouse down")
super.mouseDown(with: event)
}
}
If what you're looking for is to be able to select the text when you click (focus) the text field, you can override the class to simplify your task and you won't have to worry about locating the clicked field from the delegate.
For an NSView object, when it gets focus (ie. clicking or tabbing) it will call becomeFirstResponder so we can hook in there.
When an NSTextField becomes editable (or selectable) it grabs a reusable 'field editor' and overlays it on top of your text field during the editing. If your NSTextField has focus, you can grab this field editor using the currentEditor() call on the view.
So, once you have the field editor, you can perform selectAll on the editor to select the text.
Example class :-
class AutoselectOnFocusTextField: NSTextField {
override func becomeFirstResponder() -> Bool {
guard super.becomeFirstResponder() else {
return false
}
if let editor = self.currentEditor() {
editor.perform(#selector(selectAll(_:)), with: self, afterDelay: 0)
}
return true
}
}
Hope this helps!
I updated the ASTextField as below.
class ASTextField: NSTextField {
// ...
override func mouseDown(with event: NSEvent) {
self.sendAction(#selector(didClick(_:)), to: self)
super.mouseDown(with: event)
}
#objc func didClick(_ event: NSEvent) {
self.log.debug("did click")
}
}
In the SettingsView, I missed calling super.layout(), without which the click won't work, nor the other text field will get focus when clicked.
class SettingsView: NSView {
// ...
override func layout() {
self.log.debug("layout method")
super.layout() // This is important
}
}
NSTextField delegate methods are not required.
The method you overwrote, mouseDown(with), isn't a member of the NSTextFieldDelegate or NSTextDelegate protocols. You overwrote NSViewController.mouseDown(with).
Whenever that method is called, the thing that was clicked is your SettingsViewController's view.
To react to your textfield being selected, you use NSTextFieldDelegate .textField(_:textView:shouldSelectCandidateAt:), which you already have. The value of the textView parameter is the text view that was selected.

focus a field in a NSTableCellView for instant editing

I want to set the focus on one of two editfields in a NSTableCellView in a table with one column in a row programaticly, so that a user can instantly edit the field.
In my example I can select the row (after pressing a button), but I cannĀ“t find a way to set the focus on a special field.
I my example I want to set the cursor on the tiltle field in row 3.
class MainVC: NSViewController, NSTableViewDataSource, NSTableViewDelegate
{ var t = [Item]()
#IBOutlet weak var itemTV: NSTableView!
#IBAction func actionBu(_ sender: Any)
{
let indexSet = IndexSet(integer: 2)
itemTV.selectRowIndexes(indexSet, byExtendingSelection: false)
itemTV.scrollRowToVisible(2)
}
thanks to all helping commands, I found an solution
#IBAction func actionBu(_ sender: Any)
{ self.view.window!.makeFirstResponder(itemTV.view(atColumn: 0, row: 2, makeIfNecessary: true)?.subviews[0])
}
works fine for me,

prepareForDragOperation() not called

I've subclassed NSTextField (FileDropTextField), and created 2 of them in an NSView (in a window) in Main.storyboard.
When I drag a file from the desktop and drop it on the first FileDropTextField, it updates the field with the full path text, but prepareForDragOperation() is never called.
If I drag the same file (from the desktop) and drop it on the second FileDropTextField, the following 3 functions are called (as expected), and the field is not automatically updated with the path:
prepareForDragOperation()
performDragOperation()
concludeDragOperation()
The Swift code is as follows:
import Cocoa
class FileDropTextField: NSTextField {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code goes here.
}
override func awakeFromNib() {
NSLog("awakeFromNib")
self.register(forDraggedTypes: [NSPasteboardTypeString, NSURLPboardType, NSFilenamesPboardType])
}
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
self.stringValue = ""
NSLog("draggingEntered")
return NSDragOperation.generic;
}
override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation {
NSLog("draggingUpdated")
return NSDragOperation.generic;
}
override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool {
NSLog("prepareForDragOperation")
return true
}
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
NSLog("performDragOperation")
return true
}
override func concludeDragOperation(_ sender: NSDraggingInfo?) {
NSLog("concludeDragOperation")
}
override func draggingExited(_ sender: NSDraggingInfo?) {
NSLog("draggingExited")
}
override func draggingEnded(_ sender: NSDraggingInfo?) {
NSLog("draggingEnded")
}
}
It makes no difference if I create the second field in Main.storyboard via duplication, or if it is created (in Main.storyboard) as a separate operation. I've examined all the properties of each, and I see no difference between them (aside from object ID and constraint information).
Can anybody explain why I'm getting different behavior (when the file is dropped on the field) between these two objects when dropping the same object (desktop file) on each of them? The one that results in the call to prepareForDragOperation() is what I expect as correct behavior.
(I'm using Xcode 8.1, Swift3, on macOS Sierra 10.12.1)
Thanks!
have u tried to defocus on the first textfield, and drag drop again?
if it works, then i guess it because there's an invisible editor field is right on the top of textfield when it being focused, which makes it impossible to trigger delegate method.
so instead of using textfield, try to use textview

in swift how to dismiss the key board

I am wondering how to dismiss a key board if the user touches outside the UITextField. I am using Xcode 6.1. I added a UITextField to a UIViewController as per the below thru ViewDidLoad() function. Any help on dismissing the key board would be much appreciated.
class PickerdemoViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate, UITextFieldDelegate{
var textBox1 : UITextField!
override func viewDidLoad() {
//..................adding text box.........................//
self.textBox1 = UITextField (frame: CGRectMake(100, 152.5, 50, 35))
textBox1.delegate = self
textBox1.backgroundColor = UIColor.whiteColor()
textBox1.placeholder = "enter"
textBox1.keyboardType = UIKeyboardType.DecimalPad
self.textBox1.resignFirstResponder()
textBox1.keyboardAppearance = UIKeyboardAppearance.Default
self.view.addSubview(textBox1)
super.viewDidLoad()
}
You need to have a reference to the UITextField so make a property value like this
class MyClass: UIViewController {
var textBox1: UITextField!
...
// create your textfield where ever you were by assigning it to self.textBox1
}
Then to dismiss the keyboard you resign its as the first responder.
self.textBox1.resignFirstResponder()
Update to dimiss keyboard
Dismissing on return/done with the textField delegate method
func textFieldShouldReturn(textField: UITextField) -> Bool {
self.textBox1.resignFirstResponder()
return true
}
Dismissing on a button click (custom IBAction method)
#IBAction func buttonClicked(sender: UIButton!) {
self.textBox1.resignFirstResponder()
}
This will dismiss the keyboard by tapping screen. Make sure to not put it in the viewDidLoad.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { // this func lets you close keyboard by touching screen
self.view.endEditing(true) // this line lets you close keyboard by touching screen
}