I have a file (let's say a PDF) I have stored to disk on my macos Cocoa application. I have the NSURL that contains the path to this file.
I am currently showing that this file exists to the user using an NSView, which contains a label displaying the filename, and an NSImageView, displaying the file's icon (using NSWorkspace.shared.icon(forFile:url.path)).
My question is, how can I allow the user to 'select' or 'highlight' this NSView and when the space bar key is tapped have the QuickLook preview pop up for that particular file.
In addition, how would I have the NSView be draggable, with the end result of being able to drag the NSView to a Finder window and have the file copied to the dragged destination.
For both the QuickLook and dragging functionalities, I have gone through the Apple documentation but haven't found anything that accurately describes what I'm after.
For "selecting" a view, you should have the view accept first responder, and draw a focus ring (or whatever highlight is appropriate for you) when the view is first responder. Override acceptsFirstResponder (return true), becomeFirstResponder and resignFirstResponder (keep a flag for whether the view is first responder, and trigger drawing with setNeedsDisplay or whatever is appropriate), and the drawing mechanism (drawRect or whatever is approproiate if you're using CALayers).
Override keyDown for handling the spacebar.
Override mouseDragged and initiate a drag session, and simply create an NSDraggingItem with the pasteboardWriter being the NSURL itself.
For displaying QuickLook: QLPreviewPanel https://developer.apple.com/documentation/quartz/qlpreviewpanel
Related
I am working with an NSTableView in a macOS application and am trying to keep an arrow image next to the header drag bar of the currently sorted column. I have successfully been able to place the arrow next to the drag bar, but it only updates its position when the drag is released. I need it to update its position while the column-drag/resize is happening.
I have tried using the tableView(_:mouseDraggedWith:), mouseDragged(with event: NSEvent) and tableView(_:didDrag:) delegate methods, but the mouseDragged functions don't trigger during column resizing, and the didDrag method triggers only upon mouse release.
I have also attempted to use the NSTableView.columnDidMoveNotification notification, but it also only triggers after the drag is released.
How can I detect the drag or resize of a column while it's happening, prior to the mouse click being released?
NSTableView has a method, setIndicatorImage(_:in:), that puts an image at the right end of a column header (where the sort indicator may be displayed).
I have an NSDocuments & storyboard app created with the wizard in Xcode 8. In the ViewController Scene I have added a NSTextView. Certain menu items are disabled, such as Bold and Italic.
Looking at the First Responder the actions for bold and italic are not there. Am I supposed to write these methods myself? Is this due to the responder chain not being correctly set up? Why does underline show up but not bold?
Edit: Adding an image to show how I can edit text with the Inspector Bar, but the Format menu does not show the commands I would expect.
There is a historical (?) reason of this problem.
When the main menu was created in a xib file, xib file automatically contained a NSFontManager instance and such menu items like Bold were connected to it.
However in a modern storyboard, there is no preset NSFontManager instance.
Well then, you can connect them to a FontManager manually following the following steps.
Create a normal Object instance (blue cube) in the Application scene.
Change class of the Object instance to NSFontManager.
Connect the menu items to addFontTrait(_:) action of the fontManager. Likewise, connect "Bigger" and "Smaller" items to modifyFont(_:).
You also need to set menuItems' tag, however they are actually already set. Set the correspondent tag also manually only if menuItem's tag is 0.
If you ctrl drag from menu item to first responder, in menu view, you get the same options, just ctrl click. Then you implement whatever function you just connected. If you connect File > New to newDocument and implement in your ViewController
func newDocument(_ sender: Any?){
print("func newDocument(_ sender: Any? \(String(describing: sender)))")
}
It will get called. First responder lists all the same methods as the added Object with NSFontManager as class. I don't use #IBAction in front of method because I don't connect it.
I have a view with a textfield at the top, a textview under it and two buttons below the textview.
The keyboard is configured with a "Done" button. Once the user has typed in their info, they click the save button, which is below the textview. First they click Done to hide the keyboard (and reveal the save button) then click the save button.
I need to allow carriage returns in the textview but "Return" is already taken up by Done.
How is it usually handled when you need a Return key and ability to also hide the keyboard?
Drag a button into your view, delete the text, and resize it to take up the entire view. In the document outline, select the new button and drag it to the top of the list of elements. This puts it in the background so it is not hiding the elements of your view.
Add this code to your ViewController:
#IBAction func hideKeyboard(sender: AnyObject) {
self.textField.resignFirstResponder()
}
Link the button to this action and you're all set.
If you're using a UINavigationBar or have other buttons or fields, activating any of those UIControls could be detected and used to dismiss the keyboard via resignFirstResponder(). In fact Save/Cancel/Done are UIBarButtonItems and are a standard mechanism for completing things and changing state, and create a framework for accomplishing what you want. If you don't take that approach then you have to get creative with how you do it, and also make it clear to the user what needs to be done.
In Interface Builder you can change the type of your main view from UIView to UIControl and then use addTarget() to detect touch events as a 'touch outside' area and use those actions to resign first responder as well. But you might want to consider a UINavigationBar or some other button bar or tab interface to make state transitions.
Also review iOS Human Interface Guidelines document. It's a great document for understanding how iOS is designed to handle common situations like what you are dealing with, and it can get you out of design ruts. It's well written and worth re-visiting periodically.
I have a somewhat complex iOS view hierarchy. One piece of text is an editable UITextField. When the user touches it, it becomes first responder, and is editable.
Here's the rub, though: Best practice should be that a touch anywhere outside the edit control causes it to resign first responder and end editing. What's the best way of accomplishing this?
Techniques I've tried:
Use the exclusiveTouch property, which stops the user from interacting with other controls, but doesn't cause editing to end. Also disallows user from interacting with my toolbar "Done" button.
Put a see-through UIView under the text field control and on top of everything else (except the toolbar), and use touches there to end editing. This works, but I end up reparenting the text field onto this other random view which sits above my whole hierarchy, which means I have to take care of the text field's layout in multiple places, since it no longer lives in the place where it lived originally, and I have to delegate all its behavior back and forth from its "shield" view to its native home container, which has all the related logic.
Is there an elegant solution to this problem that I'm missing? I figure it must be a common design issue.
Thanks.
Tile 4 "see-thru" views around the textview to capture/ignore touches. Doesn't require modifying or "lifting" the textview, and can be added to the parent view in a fairly modular way.
You can't mask a region without knowing what that mask will cover and what the mask will not cover. So any solution will require enough reach to gather both of those bounds. Either pass the text rect up, or the view rect/region to be disabled down, or both to something in-between. The controller for the stuff to be covered seems as good a place as any to consolidate both rects or regions, if not the controller for the text view.
The nub of the issue is what constitutes "best practice". The fact that the keyboard remains unless the user dismisses it is deliberate. For example, many apps need the user to be able to tap a button while still working in a text field.
The keyboard has a Return button. "Best practice" is to respond to the user tapping that button by resigning first responder. Otherwise, you should leave the keyboard there, since that's what the user expects.
However, if you insist on doing it your way, there's a simple solution: put a UITapGestureRecognizer on the background view. Its handler will be triggered if the user taps on the background or on any button or similar in the interface. So, presuming you have kept a record of what the first responder is, you can send resignFirstResponder to the first responder in the tap gesture recognizer's handler.
If you change your base view to a UIControl you can add an IBAction to that layer that resigns your text field as first responder.
Also, if you have multiple touch events, make sure they each becomeFirstResponder when touched.
I'd love to have some more details to qualify my explanations xD
I'd like to create a UITextView that you can tap anywhere within it and start typing at that location. The default behavior of the control is that typing starts where the last character ended. So, if I had a UITextView with no text in it and tap in the middle of the control, I'd like typing to start there--not in the upper left.
What is the best way to implement this behavior? I've considered making the default text value of the view to be 3000 space characters or something similar, but this seems like not an elegant solution. Suggestions?
I suggest deriving from UITextView to create a custom view that handles taps. You'll want to override the following methods, probably:
touchesBegan:withEvent
touchesMoved:withEvent
touchesEnded:withEvent
touchesCancelled:withEvent
Make sure the userInteractionEnabled property has a default value of YES. Override hitTest:withEvent and pointInside:withEvent to figure out where in your view the user tapped.
Be sure and read the Responding to Events section in the View Programming Guide for iOS, and also see the Event Handling Guide for iOS for more details.
Anyway, once you figure out where the user touched, you can modify the text or reposition the karat as appropriate.