I've been migrating my code to Swift and now I'm trying to modernize the UI by switching to view-based table views (OS X). With the cell-based tableview, providing accessibility title, etc. was as easy as overriding the func accessibilityTitle() on the cell (it was a subclass of NSTextFieldCell) - but unfortunately, with NSTableRowView, it's not as easy.
I've overridden pretty much everything I can think of, but the VoiceOver still reads "Table View Cell" and nothing else. The overridden methods don't even get called.
I've also overridden func isAccessibilityElement() -> Bool, func isAccessibilitySelectorAllowed(selector: Selector) -> Bool, func accessibilityIsAttributeSettable(attribute: String) -> Bool to always return true. These methods don't even get called.
I've also tried for the cell to conform to NSAccessibilityStaticText, but that's not possible in Swift, so I've tried a hack, overriding conformsToProtocol(_:), but that never gets called either.
Another thing I've tried was overriding func respondsToSelector(_:) to check what selectors is the runtime checking, but alas nothing helpful gets checked.
Is there anyone who's tried solving this? All I want is to supply VoiceOver with a custom string to be read out...
Related
I want to use kind of SwiftUI-like functional style of configuring the UI, for example by providing [a global] method hide(view: UIView) instead of writing view.isHidden = true.
Now this works fine but for reverse logic I want to have the global method show() but within context of an UIViewController it conflicts with the class method show(sender: Any).
Is there any language trick I can you in order not to have to write MyAppName.show() each time I use it?
Your title is a bit inaccurate. You already know how to disambiguate it. The question is about how to do so without repeating the module name. Unfortunately, I don't think there's any such way.
I found out that Swift compiler seems to be overreacting, as one method has 2 arguments and my method has 1 argument. What's the reason to write "nearly matches" and breaking compilation is unclear to me.
However, I found the solution:
extension UIViewController{
func show(_ view: UIView) { view.isHidden = false }
}
The documentation for the NSTableViewDataSource protocol says
This method is mandatory
and if you create a new NSTableViewDataSource the compiler asks you to provide this method as well as numberOfRows(in tableView: NSTableView).
So far, so good. And if you provide it, it promptly gets called once for every row... but if you delete it, the table seems to work every bit as well as it does without. There seems to be absolutely no connections in a view-based table to this method: in the NSTableViewDelegate's tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) method, I return new views based on the content array of my datasource, and if I stick a bunch of nonsense data into objectValueFor: row (up to and including returning the same string - my table objects are not strings) is has no effect whatsoever because I'm setting the values of my NSTableViewCells in viewFor tableColumn:.
If I'm handling selection changes (and anything I want to do with objects) by retrieving the corresponding value from the datasource directly (e.g.)
func tableViewSelectionDidChange(_ notification: Notification) {
print(dataSource.allItems[tableView.selectedRow])
}
is there any reason to implement this method? I cannot see any point where that object is actually used, and it feels like an artefact from cell-based tables.
I don't want to break my code by leaving out a necessary method, but since I'm about to try something weird and wonderful with a custom datasource, I also don't want to overcomplicate my code with a method that gets called, but the result of which seems completely meaningless.
If the cell view responds to setObjectValue:, then the table view calls that method and passes in the object value for the row that was obtained from tableView(_:objectValueFor:row:).
NSTableCellView does respond to that method and is a common cell view class. NSTextField does, too; it actually inherits it from NSControl.
In your case, your cell view either doesn't have an objectValue property or, more likely, you're just not using it.
One common configuration is to use NSTableCellView as the cell view and then use bindings to bind the subviews to key paths going through its objectValue property.
I am having difficulty implementing an action for an NSMenuItem in Swift. Normally, you implement the action like this in Objective-C:
- (void) asdf:(id)sender
This works perfectly fine, after setting up the action in the first responder like so:
However, after rewriting my view controller in Swift, the following new method doesn't seem to be called:
func asdf(sender: AnyObject?)
It doesn't seem to work, even though both the Obj-C and Swift versions are for the same view controller subclass.
In Swift 3.0 you'd define it as:
func asdf(_ sender: Any)
Why?
If you use _ you can drop parameter name when calling a function, so now you can call it like:
object.asdf(object)
Instead of:
object.asdf(sender: object)
Moreover, with Swift you'd use Any instead of AnyObject in this context. You can find more on differences between those here.
Does anybody know why the ZoomingPDFViewer Apple Sample Code project no longer works? It was working prior to the iOS 10 release but now it keeps returning a unrecognized selector error when calling [PDFScrollView setPDFPage:].
It seems like the custom classes set in the storyboard are no longer being instantiated.
I had the same issue. It seems that the auto conversion to Swift 3 doesn't work well with IBs.
There are two options:
a) You can set the argument label--i.e.the parameter name to be shown externally--to _. For example,
#IBAction func setPDFPage(_ sender: AnyObject) {
// ...
}
b) You can remove the IBAction connection in the IB and re-set it. Notice instead of the previous setPDFPage(sender:) the connection will say setPDFPageWithSender(sender:) or something like that. (I didn't actually try it with the code in question, but writing from experience here.)
The same is true for segues.
I would like to add copy (Command + C) functionality to my NSTableView for a row (or multiple rows) that the user has selected. Based on what I read the copy functionality in the Menu Item should be active automatically if you implement copy in your TableView Controller Delegate.
I've tried adding in the NSTableViewController (is the delegate and data source of the table view) the following things with no luck, the copy menu item still appears greyed out:
override func copy() -> AnyObject {
print("TEST: copytriggered") /Here i would copy to clipboard the selected rows/
return true
}
2.
Conforming to NSCopying, implementing copyWithZone...
3.
Trying 1 and 2 in the window controller and app delegate.
Adding func tableView(_ tableView: NSTableView,
pasteboardWriterForRow row: Int) -> NSPasteboardWriting? in the TableView data source.
I have no clue what to do at this point, any help appreciated.
Thanks,
Marc
Just got the answer, publishing here for future reference.
You are confusing the Copy edit action method with the methods for making a duplicate of an object. The NSCopying protocol and the copyWithZone() method are for the latter, enabling an object to create a copy of itself. Likewise, the copy() method is just a convenience cover method for copyWithZone().
The action method for the Copy edit menu item is subtly different. It's "func copy(_ sender: AnyObject?)". That's the general form for all action methods, taking a sender argument and not returning anything. You need to implement that method.
Finally, tableView(_:pasteboardWriterForRow:) is generally only used for drag-and-drop, when the user drags a row.