Update NSTouchBar on the fly to add/remove items programmatically - swift

I'm currently implementing the NSTouchBar api to my macOS application.
At this moment, the only touch bar I have has the main View Controller as its delegate and I can add items to it fine. The catch is, I need some of those items to appear only when a certain condition is met (a row is selected in a table).
Consider I have a boolean that indicates whether or not the button should be visible. How do I update the NSTouchBar on the fly to show/hide this button in case my boolean changes? (I don't need to observe this boolean, I could simply make the call to update in another method I already implemented)
What I did for now is the following: in touchBar(:makeItemForIdentifier), I have a switch for all identifiers, and under the proper case, I either return the NSCustomTouchBarItem with the button, or nil if my boolean is false.
I tried calling makeTouchBar again after a row of the table is selected but it doesn't update the buttons' visibility, as if touchBar(:makeItemForIdentifier) is not called again.
Thanks!

Four ideas:
Try changing your touch bar's defaultItemIdentifiers to the set of item identifiers that should be shown. Note that this would be problematic if the user has customized the touch bar, but I think swapping items on-demand and customizing the touch bar doesn't go well together anyway. This also has the advantage that you don't need to return nil in touchBar(:makeItemForIdentifier:).
Calling makeTouchBar() will create a new NSTouchBar instance, but not change the touchBar property. Try something like
viewController.touchBar = viewController.makeTouchBar()
or
viewController.touchBar = nil
Set the touchBar property on the NSTableRowView that should show extra items when selected, and make sure to include the otherItemsProxy in your defaultItemIdentifiers. As the contents of the touch bar are comprised of all elements in the responder chain, this might include the touchBar property of the table row (provided it can become first responder).
Are you sure that these items should be hidden when the row is not selected? Consider disabling them instead (e.g. by setting the enabled property of the buttons they contain to false).

Just invalidate the touchbar in your view controller:
self.touchbar = nil
The delegate method makeTouchBar() will then automatically be called. Using your flags, you can easily choose the icons to appear.
EDIT: this solution has been tested and works fine.

Related

ScrollView.refreshControl = refreshControl does not show custom settings. Function executes, but no display feedback

I have a ScrollView with 3 collection views within it. I implemented a refresh control. The refresh control correctly functions in the sense that it triggers the required function - and even pulls down the scroll view with a delay.
However, setting any of the refreshControls properties (such as text/background colour) have 0 effect when I add the refresh control to my scrollView like so:
scrollView.refreshControl = refreshControl
When I use the old method:
scrollView.insertSubview(refreshControl, at: 0)
I am able to see the text/spinner, but it's buggy.
Is there a reason that the refreshControl's properties might be hidden from view when not using insertSubview?
Thanks to a user named Matt over on this question:
UIRefreshControl not showing in landscape when in a navigation controller with large titles
I was able to identify that this indeed was caused by Large Title Preferred: Automatic set in Storyboards for my navigation titles. Even though my nav bar is hidden on the view controller, the undesired effect of it hiding my refreshControl still existed. To fix this, as Matt says:
"If you turn off Prefers Large Titles everything is fine. I've played with some other settings, but no effect."

Select the first element in view for Voice Over

I am using Xcode to make an iOS app. When I segue into the next view controller, it has objects in the following order from top to bottom on the screen: label, collection view, button. When I use voiceover, I want the first item on the screen to be in focus (the label). However, whatever I do, it is always a particular cell from the collection view that is selected by default (somewhere in the middle of the screen). In landscape it is a different cell and in portrait a different cell. But every time it is that same cell. I tried using UIAccessibilityPostNotification, as well as using delays and other things. Nothing seems to work.
Should be posted when a new view appears that encompasses a major portion of the screen.
Optionally, pass the element that VoiceOver should move to after processing the notification.
UIKIT_EXTERN UIAccessibilityNotifications
UIAccessibilityScreenChangedNotification;
You need to provide
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, youLabel);

How to handle modifier keys in Cocoa menubar application

So I have a menu bar application that has a slider inside a custom view: I want the ability to press the option key while dragging to make the slider snap to increments.
Right now, everything is working... except I cannot find a single way to notify my menu app that the option key is being pressed. Here's what I've tried:
NSEvent.addGlobalMonitorForEvents
"Note that your handler will not be called for events that are sent to your own application."
NSEvent.addLocalMonitorForEvents
"Your handler will not be called for events that are consumed by nested event-tracking loops such as control tracking, menu tracking, or window dragging"
NSApplication.shared.currentEvent
This dosesn't work because the flagsChanged event isn't handled by the app, so this value is set to another value (mouse button presses).
flagsChanged(with event: NSEvent)
This doesn't get called. I've set the viewController to acceptFirstResponder as well as resigned the first responder of the view. Also, I've subclassed the view and overridden flagsChanged and that hasn't worked.
I've tried creating a menu item that has a key like "s" or "p" to trigger an action. But when the "action" gets triggered, the menu bar hides...
So I'm trying to find how I can be notified when the option key is pressed. Is this possible? I can clarify if you have any questions.
In your custom slider view's mouseDragged method, you'd simply check the NSEvent parameter's modifierFlags to see if it contains the option key.
--
Since it could be interpreted that the slider is an actual NSSlider, if that's the case then the slider's control is really implemented in with an NSSliderCell, and the mouse tracking gets handed off from the view to the cell within mouseDown, so the NSSlider's mouseDragged wouldn't be called, and instead you'd have to do work in the cell's trackMouse:inRect:... method. But if you're using an NSSlider, then you'd probably simply want to use altIncrementValue.

Custom MKAnnotationView - How to capture touches and NOT dismiss the callout?

I have a custom MKAnnotationView subclass. It is showing the view exactly as I want it to. In that view, I have a button. I want to capture events on the button to perform an action. This works just fine.
However, I do NOT want the callout to be dismissed or disappear. Basically, touching the button in the callout will start playing a sound, but I want to leave the annotation up so the user can press stop if they want to, without having to touch the map pin again to bring the annotation back up. In another instance, I want the button touch to animate more details in the callout, so I definitely don't want to dismiss the callout at that point.
How can I keep the callout from disappearing whenever the user selects the callout or a button inside the callout?
This may not be the best solution, but it definitely works. First off, I tried a number of things, like observing for context and such, but I never got past crashing, and it seemed cumbersome. So, this is what I did:
I first specified what the controlling factor was for keeping an alert viewable. In my case, I created a custom annotation view, and whenever the user clicks a button on that custom view, I want it to stay visible, and maybe even change the content. So, I set a delegate on that custom view so that my map can know when something changes. In my map view controller, I catch that message and set a class member variable to true to signify that I want the annotation view to stay.
NOTE: This will happen before the selection messages occur.
Now, in my didDeselectAnnotation method, I check the boolean value. If I want to keep it visible, I opt to NOT remove my annotation, I reset the boolean value, and I re-select the annotation manually, setting animation to NO. This lets the annotation view "stay" visible--maybe a cheat, but the user can't see the difference. Whenever that boolean value says that deselection is ok, I simply remove the annotation and all is well.
So, the workflow is this:
Touch pin
CustomAnnotationView is displayed
User clicks a button on CustomAnnotationView, which notifies the delegate (mapView) that the
action occurred
Set the class boolean value to know that you want to keep the annotation around
mapView then calls didDeselectAnnotation method
In didDeselectAnnotation method, use conditional to decide if you should remove the annotation, or keep it around by not removing the annotation and manually re-selecting it without animation.
I hope this helps others. It took me a while to figure this out, so I hope it saves you time.
If you find a better solution, by all means, please post it here!
you can suppress callout closing by using hittest, check this following post.
Detect tap on title of callout

Detect what's being inputed in a UITextField

I have a UITable View with a textfield that is editable right on the view (like Phone in contacts, etc.). I want to enable/disable my save button conditional up text being present in this field. So, I want the button to start out as disabled (for a new record) and then, as soon as I type the first letter into my text field, I want the button enabled. If I delete again back to zero, I would like the button disabled. You get the point.
Now, for doing this I need some way to detect the text being inputed while the user writes it (and when he finishes editing).
Does anybody know how to do this?
Thanks a lot. Still noob...
Try this: (from the Apple documentation for UITextInputTraits)
enablesReturnKeyAutomatically
A Boolean value indicating whether the return key is automatically enabled when text is entered by the user.
#property(nonatomic) BOOL enablesReturnKeyAutomatically
Discussion
The default value for this property is NO. If you set it to YES, the keyboard disables the return key when the text entry area contains no text. As soon as the user enters any text, the return key is automatically enabled.
Have your view controller adopt the UITextFieldDelegate protocol, and then implement a couple of the protocol's methods:
– textFieldDidBeginEditing:
– textFieldDidEndEditing:
Also, be sure to set the text field's delegate property to point to your view controller. The text field will then automatically send these messages to the controller when editing session begins and ends.