Correct behaviour of Mouse down inside row of NSTableView? - swift

I have a NStableView which is NSView based. In each row also have a NSImageView which is subview of NSView. I have subclassed this NSImageView then overrided mouseDown method. The problem is tableViewSelectionDidChange is also getting fired when user clicks on this imageview. I want only mouseDown to be called but not tableViewSelectionDidChange.
If I set selectionHighlightStyle of table to NSTableViewSelectionHighlightStyle.None then only mouseDown of image view is called. And if I don't give selectionHighlightStyle to NSTableViewSelectionHighlightStyle.None then both mouseDown of imageview and tableViewSelectionDidChange are getting called. Setting selectionHighlightStyle to none seems to solve my problem but is this correct approch? Or I am getting this behaviour because of some bug in appkit?
Also I can't find this behaviour documented somewhere.

I think the correct approach you be to subclass NSTableView and override the NSResponder method -(BOOL)validateProposedFirstResponder:(NSResponder *)responder forEvent:(NSEvent *)event
You'll have a finer control over which view will get the mouse event, and won't have to resort to "hackery" like changing the table view selection style.
From the apple docs:
Specifying How Subviews Should Respond to Events
Views or controls in a table sometimes need to respond to incoming events. To determine whether a particular subview should receive the current mouse event, a table view calls validateProposedFirstResponder:forEvent: in its implementation of hitTest. If you create a table view subclass, you can override validateProposedFirstResponder:forEvent: to specify which views can become the first responder. In this way, you receive mouse events.
The default NSTableView implementation of validateProposedFirstResponder:forEvent: uses the following logic:
Return YES for all proposed first responder views unless they are instances or subclasses of NSControl.
Determine whether the proposed first responder is an NSControl instance or subclass.
If the control is an NSButton object, return YES.
If the control is not an NSButton, call the control’s hitTestForEvent:inRect:ofView: to see whether the hit area is trackable (that is, NSCellHitTrackableArea) or is an editable text area (that is, NSCellHitEditableTextArea), and return the appropriate value. Note that if a text area is hit, NSTableView also delays the first responder action.
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TableView/RowSelection/RowSelection.html#//apple_ref/doc/uid/10000026i-CH6-SW9

Related

Custom views with UIPanrecognizer unintendetly locks UITableViewController

I would like to place custom views (created from nib) inside UITableViewCells. That works without issues, unless the custom view declares it's own single touch UIPanrecognizer. In those cases my custom view responds to the pan events, but that pretty much disables the ability to scroll the cells inside the table view. I tried registering the UIPanRecognizer with both the cell and the custom view, but that ability seems to be not allowed any more since IOS 9 (seems like this is also not how Apple wants touch events to be handled, I read somewhere that touch events should just be handled by one view)
I also failed to find a way to scroll the table view programmatically, which would also seem a little hacky.
What are my options when it comes to wanting to preserve the pan behaviour of the UITableView while still being able to respond to a pan event inside my custom view?
My use case is as follows: I want to reuse a custom view that can sideways-scroll that I created for another part of my app. My table view is set to only react to downward pans. Therefore the two pan actions are not interfering.
Two ways I have this solved currently are as follows:
The sideways scroll reacts to two-finger pans. This is super awkward and unintuitive.
All gestures on the custom view (except taps) get sent to its superview. A tap on the custom view disables the rerouting, which locks the table view and enables sideways scrolling by letting the custom view react to the sideways pan. Another tap restores the initial behavior. This is awkward since it asks the user to perform three separate actions for what should be just one.
I know that I can enable sideways panning for table views, but that doesn't solve my problem of wanting to reuse my custom view inside the cell.
Does anyone know a solution to this dilemma?
The solution was to create a custom UIGestureRecognizer.
I followed this tutorial, created a custom SidewaysPanGestureRecognizer, registered that instead and passed all gestures but SidewaysPanGestureRecognizer to the superview by implementing the UIGestureRecognizerDelegate method responsible for deciding if the view should handle the gesture:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer,
shouldReceiveTouch touch: UITouch) -> Bool {
return gestureRecognizer is SidewaysPanGestureRecognizer
}

How to make a table view based selector control accessible for VoiceOver?

I have a selector control in my app based on UITableView. The user can scroll the table view and a marker in the center shows selected item. Every selectable item is a table view cell.
Example
Now I want to make my app VoiceOver compatible for a friend. But this control, I have trouble make it work.
With VoiceOver on, I can't scroll the table view to select other elements. I looked at picker view in clocks app. It does not scroll too. But when you flick up or down it jumps to the next or previous value. It says
"swipe up or down with one finger to adjust the value".
I read Matt Gammell's VoiceOver guide where he sais the hint must say what the control does not what you should do.
So I infer this is a special trait they use for things that can swipe up or down to adjust value. But I cant find such trait.
Since UIPickerView is based on UITableViews how did Apple make it work with VoiceOver? Must I use gesture recognizer for flick?
Edit
I am setting the adjustable trait on the UITableView subclass like this:
self.isAccessibilityElement = YES;
self.accessibilityLabel = #"Start date.";
self.accessibilityTraits = UIAccessibilityTraitAdjustable;
The table view implements
- (void)accessibilityIncrement {
NSLog(#"accessibilityIncrement");
}
- (void)accessibilityDecrement {
NSLog(#"accessibilityDecrement");
}
Now I can drag across cells and VoiceOver will read their labels and mark them with the black rectangle. But table view does not scroll and the methods above don't get called.
The cells themselves are isAccessibilityElement = NO; and don't implement accessibility action methods.
You are looking for the adjustable trait: UIAccessibilityTraitAdjustable.
If you specify this trait on your view/cell you must also implement accessibilityIncrement and accessibilityDecrement in that view/cell. These are the two methods that get called when the user swipes up and down with one finger.
There is no need to implement any gesture recognizers yourself. Setting the trait is enough to get that behavior (it will also add the "swipe up or down with one finger ..." description)
You add the UIAccessibilityTraitAdjustable to the element's traits. Then you implement the -(void)accessibilityIncrement and -(void)accessibilityDecrement actions. In the case of the date picker, you should do this for each component (year, month, date) - each of these is an element (which the user can move the VoiceOver cursor to by flicking left and right) and each is adjustable (by flicking up/down while the VoiceOver cursor is on it).

resignFirstReponder by touch somewhere outside the UITextView with nested View hierarchy

my first question on stackoverflow...i'm very nervous ;)
Ok, i have troubles to make my UITextView resignFirstResponder on the moment i touch some where on the rest of my view hierarchy.
It's like this:
UIView -> UITableView -> UITableCell -> UIView (my containerview) -> UIView (my bubbleview) -> UITextView
As the UITextView isFirstResponder, we can fill in some text, and keyboard is shown. It would be nice if we do not have to dismiss the keyboard by pressing a button, but just by tapping outside the UITextView somewhere on the surface.
But how and where should i respond to a tap gesture? And on that controller somewhere in the chain. I don't have everywhere a reference to the UITextView to send it a message (resignFirstReponder) to...
I hope i can make myself clear. How can i get rid of of the keyboard by just touch somewhere on the surface....
Hopefully somebody can give some hints...
Regards,
Jeroen
You could use NSNotificationCenter to fire a notification when you touch outside of the text fields bounds to which to your text fields respond to with resignFirstResponder.
Edit with more detail:
You could implement the following method on your UIViewController/View a
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[myTextFeild resignFirstResponder];
...or send a notification etc.
}
Its a bit of a cheap way to pull it off but does work.
You might try subclassing UITextField and overriding pointInside. Examine the touch received and if it is outside of your UITextField subclass instance, then resignFirstResponder.
Edit:
The above actually won't work and I apologize for the bad advice. What I would suggest instead, is adding a transparent subview over the window, which overrides hitTest and calls a predefined callback but returns nil. In the callback, you could perform some arbitrary task (like resigning first responder from your text view) and then remove the view from the hierarchy.
There is a class named CWPrimaryViewWindow in my open source project at https://github.com/Jayway/CWUIKit that is a helper class for implementing this behavior.
The primary idea is to subclass UIWindow in order to hijack all touch events. Then inhibit all touch events not targeted for the primary view, instead firing a cancel notification. While passing through touch events to the primary view as normal.

backgroundView for a UITableViewCell that is being reordered?

I'm trying to change the background view for cells that the user starts to drag with the handle out on the right hand side.
Apparently the selectedBackground view is not used in this state, is there any other solution?
This seems to be a tough one. It doesn't seem as if Apple has provided a way to notify the developer that the user has started reordering a table view cell. So I think this is what you'd have to do:
Subclass UITableViewCell.
Override the touchesBegan et al methods (this might get you started: http://devblog.wm-innovations.com/2010/03/30/custom-swipe-uitableviewcell/)
If you detect a "drag" touch event and the cell's showsReorderControl property (see http://developer.apple.com/library/ios/#documentation/uikit/reference/UITableViewCell_Class/Reference/Reference.html) returns YES, swap out the backgroundView, remembering the previous one.
When the touch event ends, restore the old view.
Realize that it's not as easy as above 4 steps.
Custom touch handling inside a tableview is possible but hard. You might have to temporarily disable touch events in the UITableView (climbing up the responder chain) to make this work.
I hope these pointers get you moving in the right direction, though.

handling same touch event with multiple control (parent and child)

I need to handle a touch event on my custom uiviewcotroller. I have a sub controller within the view that already handle touch event (it's a plot that handle zooming and scrolling).
I want to make a tabbar disappear when I tap the screen once. Actually it only works (even tought the tabbar doesn fade away but simply is no visible) in the areas in which the subcontrol is not present but I need it to work everywhere still handling the subcontrol events.
Make sure you're calling the superclass's event handler method in your event handler method to continue propagation of the event up the responder chain.
Also make sure the subcontrol's exclusiveTouch property is set to NO.
You might want to have a look at the event handling documentation.
Try to set userInteractionEnabled = NO in subcontrol view.
UPD: Try to add transparent button to subcontrol.