I have a view that contains a subclass of a UIControl.
Occasionally I want to add a tap gesture recognizer to the view that should override any other touches on the children views.
By default gesture recognizer should cancel all other touches in view. However, my UIControl still gets triggered. Is that expected behavour?
I haven't found an answer to this particular problem. So the solution in my situation was to use a different combination of actions to achieve the desired outcome.
Related
I'm surprised that I haven't been able to find an answer for this from searching. So if there is a page describing how to do this, let me know, but I've been unable to find it.
I have 3 sibling NSOutlineViews all inside an NSSplitView I have added observers for NSOutlineViewSelectionDidChange which, when triggered update an NSTextView editor. I even test for negative row values to indicate that the user has unselected the row and clear the text.
The NSOutlineViews are connected to custom datasource objects and the NSSplitView is created by my custom NSWindowController in turn created by my custom NSDocument and custom NSDocumentController (I'm not using .nibs)
However I cannot seem to receive triggers for the NSOutlineViews changing their active status, it works if the user selects a different row in a different NSOutlineView, as the selection has changed but if they click the selected row of a different view, I don't receive any event or notification that anything has changed. Visually I can see the change as the row selection highlight colour changes from coloured to grey, in the outline view that has lost focus and the selection row colour changes from grey to coloured in the newly activated view.
I've tried to catch mouseDown events, tried becoming first responder, tried observing changes in the NSSplitView I've been right through the NSObject hierarchy from NSOutlineView to NSResponder looking for the appropriate notification or method. I found deprecated documentation regarding a focus change notification. I've tried combinations of nsview, nsnotification, nsoutlineview and various actions in google but can't find the 'this is how you do it'
EDIT:
this is the code I've added to my NSOutlineView subclass (along with prototypes in the headers) to become first responder, but it is never triggered.
- (BOOL)acceptsFirstResponder { return YES; }
- (BOOL)becomeFirstResponder {
NSLog(#"becomefirstResponder %#",self);
return YES;
}
The issue is as follows: I have an iOS 9 app (Swift) with a nav bar that houses a bunch of UIBarButtonItems with segues to several UINavigationControllers that finally display tables, views etc. as popovers. Of course tapping one of these buttons does not automatically disable the other UIBarButtonItems in the nav bar and the user is free to tap them. With a popover already displaying however we get the following warning as this is against the HIG and also grounds for rejection:
Attempt to present UINavigationControllerViewController which is already presenting (null)
To take care of the issue once a popover is displaying a) the remaining UIBarButtonItems need to be disabled and b) displaying additional popovers needs to be halted.
So what is the best cause of action here? The isBeingPresented flag of the popover's view controller is not available in the governing view controller because one can't create outlets of one view controller in another and the navigationController's viewControllers array doesn't include external navigation controllers or their descendants.
My lame workaround involves a global appIsPresentingPopover Bool which is set to true in viewDidLoad and to false in viewDidDisappear in the mentioned view controllers. That Bool is then tested in the governing view controller's prepareForSegue method to stop additional popovers from displaying. It is also kvo'ed to disable any enabled UIBarButtonItems which now all have an outlet. Ugly!
What is the standard way of handling this common scenario?
Surprisingly simple solution and much more elegant than my initial attempt. I added segue identifiers for all involved popups and a UIViewController class variable. In prepareForSegue I set the variable to the destination view controller. Then in shouldPerformSegueWithIdentifier I simply check whether that popover variable is nil. If it is not, I dismiss the UIViewController and set it to nil. Finally I also followed Beau Nouvelle's suggestion and kept those buttons lit so users can switch directly between popovers. Thanks again.
I'm trying to implement an NSSplitView similar to Xcode where you can collapse a view by dragging its handle to under half its width and it will collapse automatically. I have that part working, but I need to update the state of a button in the toolbar when this happens. I tried listening to splitViewDidResizeSubviews and checking if the splitView's view is collapsed, but that method fires 16 times with collapsed == true, so I don't think I want to update the button's state 16 times. Is there a cleaner way to do this? I'm new to Cocoa, but from what I've seen, I would expect there to be some way to just say bind this button's state to the isCollapsed property and be done with it. Does such a thing exist?
If you subclass your NSSplitViewController you can add a listener for the SplitViewItem's isCollapsed property:
class MySplitViewController: NSSplitViewController {
var observer: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
let sideViewSplitViewItem = splitViewItems[0]
observer = sideViewSplitViewItem.observe(\.isCollapsed, options: [.initial, .new]) {splitViewItem, _ in
print("Sidebar collapsed state changed to: \(splitViewItem.isCollapsed)")
}
}
}
The best way to see what bindings are available is to check the docs, specifically the Cocoa Bindings Reference (look in the sidebar for the view you're after).
NSSplitView doesn't have the binding you describe, so I think you're on the right track with your current approach. Of course, you don't need to update the button's state sixteen times, just check it's value each time, and update it if needs be. It might seem a bit wasteful, but checking the value of a bool is a very cheap operation, and you won't notice any kind of performance hit.
While NSSplitView has no event or observable property for when one of it's subviews is "collapsed", the immediate subview itself will have its hidden property set to YES.
So you can either observe the subview's hidden property yourself, or if you're creating your own custom subview of NSView, you can override the -(void) setHidden:(BOOL)hidden to catch the event.
I do the latter, and it works correctly every time.
-(void)setHidden:(BOOL)hidden
{
[super setHidden:hidden];
// Insert code to react to the "collapsed" event here.
// (You're likely going to tell your parent view,
// self.window.windowController, or something.)
}
Of course the setHidden method / hidden property can in theory be called for purposes other than from NSSplitView "collapsing" your view, but this is very unlikely to ever happen in practice.
I've spent the last couple of days reading through docs and answers trying to get a solution for this. So I resort to this, with a probability of being a duplicate, as a cry for help.
I have a GestureOverlay panel to read horizontal swipe gestures. This sits on top of a view pager.
I need to disable the functioning of the view pager when the touch event is read on the gesture panel.
I've extended ViewPager to let me enable and disable it with a member method.
Overrode the onTouch event of gesture to return true (consume the event) and disallow Intercept touch event on its parent (only on action_move/down). I also tried to disable the view pager onTouch, but the viewpager's on touch event is fired first
How do I achieve this?
I have a window with a content view that does several things, including managing an iOS like navigation controller.
The window's content view front most view is an invisible custom view set here to manage drag n drop all over the window, called DragNDropView.
Now I want to manage the swipe gesture to navigate (left swipe only) in the navigation controller.
Unfortunately the DragNDropView catches the gesture (if I implement -(void)swipeWithEvent: and set acceptTouchEvents:YES on it), and I don't want that since I want the views behind it to catch it, so of course I set acceptTouchEvents to NO on the DragNDropView.
I'm suprised the gesture event does not "go down" the view hierarchy as the views below do have their acceptTouchEvents set to YES when the front most view has acceptTouchEvents set to NO.
If the front most view does not handle the gesture, the gesture event is not passed to the views behind it????
gestures handling seems to be in NSResponder, so I even expect my NSViewController subclass to handle it, but it seems I can't.
Can any one help me understanding the problem?