Could anybody suggest a method to select all the text of an NSTextField when the user clicks it?
I did find suggestions to subclass NSTextField and then use mouseDown or firstResponder,` but it's beyond my skill for now. So I was hoping either there would be an easier solution or somebody might be kind enough to detail the steps required.
There isn't an easier solution, you need to subclass NSTextField to do what you want. You will need to learn how to handle subclassing if you are to do anything useful in Cocoa.
Text fields can be relatively complex to subclass because an NSTextField uses a separate NSTextView object called the Field Editor to handle the actual editing. This text view is returned by the NSWindow object for the NSTextField and it is re-used for all text fields on the page.
Like any NSResponder subclass, NSTextField responds to the methods -acceptsFirstResponder and -becomeFirstResponder. These are called when the window wants to give focus to a particular control or view. If you return YES from both these methods then your control/view will have first responder status, which means it's the active control. However, as mentioned, an NSTextField actually gives the field editor first responder status when clicked, so you need to do something like this in your NSTextField subclass:
#implementation MCTextField
- (BOOL)becomeFirstResponder
{
BOOL result = [super becomeFirstResponder];
if(result)
[self performSelector:#selector(selectText:) withObject:self afterDelay:0];
return result;
}
#end
This first calls the superclass' implementation of -becomeFirstResponder which will do the hard work of managing the field editor. It then calls -selectText: which selects all the text in the field, but it does so after a delay of 0 seconds, which will delay until the next run through the event loop. This means that the selection will occur after the field editor has been fully configured.
The answer from #Rob presumably worked at one point, but as #Daniel noted, it does not work any more. It looks like Cocoa wants to track the mouse and drag out a selection in response to a click, and trying to select the text in response to becomeFirstResponder does not play well with that.
The mouse event needs to be intercepted, then, to prevent that tracking. More or less by trial and error, I've found a solution which seems to work on OS X 10.10:
#interface MyAutoselectTextField : NSTextField
#end
#implementation MyAutoselectTextField
- (void)mouseDown:(NSEvent *)theEvent
{
[[self currentEditor] selectAll:nil];
}
#end
As far as I can tell, by the time mouseDown: gets called the field editor has already been set up, probably as a side effect of becomeFirstResponder. Calling selectAll: then selects the contents of the field editor. Calling selectText: on self instead does not work well, presumably because the field editor is set up. Note that the override of mouseDown: here does not call super; super would run a tracking loop that would drag out a selection, and we don't want that behavior. Note that this mouseDown: override doesn't affect selection once the textfield has become first responder, because at that point it is the field editor's mouseDown: that is getting called.
I have no idea what range of OS X versions this works across; if you care, you'll need to test it. Unfortunately, working with NSTextField is always a little fragile because the way field editors work is so strange and so dependent upon the implementation details in super.
Some updates with Swift:
import Cocoa
class TextFieldSubclass: NSTextField {
override func mouseDown(theEvent: NSEvent) {
super.mouseDown(theEvent)
if let textEditor = currentEditor() {
textEditor.selectAll(self)
}
}
}
Or for precise selection:
import Cocoa
class TextFieldSubclass: NSTextField {
override func mouseDown(theEvent: NSEvent) {
super.mouseDown(theEvent)
if let textEditor = currentEditor() {
textEditor.selectedRange = NSMakeRange(location, length)
}
}
}
Swift version, works for me:
import Cocoa
class TextFieldSubclass: NSTextField {
override func becomeFirstResponder() -> Bool {
let source = CGEventSourceCreate(CGEventSourceStateID.HIDSystemState)
let tapLocation = CGEventTapLocation.CGHIDEventTap
let cmdA = CGEventCreateKeyboardEvent(source, 0x00, true)
CGEventSetFlags(cmdA, CGEventFlags.MaskCommand)
CGEventPost(tapLocation, cmdA)
return true
}
}
I like Konstantin's solution, but that one will select on every mouse down. Here's a variation I'm using to select in mouseDown method, but only if it just became the first responder:
class SelectingTextField: NSTextField {
var wantsSelectAll = false
override func becomeFirstResponder() -> Bool {
wantsSelectAll = true
return super.becomeFirstResponder()
}
override func mouseDown(with event: NSEvent) {
super.mouseDown(with: event)
if wantsSelectAll {
selectText(self)
wantsSelectAll = false
}
}
}
I know this is an old question, but here's an update for those who might stumble over this.
Override NSTextField and hook into becomeFirstResponder(). You won't have to worry about managing delegates or clicks. The tricky part is finding the field editor for the focused text field first before asking it to select all the text.
Objective-C
// in AutoselectOnFocusTextField.h
#interface AutoselectOnFocusTextField : NSTextField
#end
// in AutoselectOnFocusTextField.m
#implementation AutoselectOnFocusTextField
- (BOOL)becomeFirstResponder {
if (![super becomeFirstResponder]) {
return NO;
}
NSText* fieldEditor = [self currentEditor];
if (fieldEditor != nil) {
[fieldEditor performSelector:#selector(selectAll:) withObject:self afterDelay:0.0];
}
return YES;
}
#end
Swift
class AutoselectOnFocusTextField: NSTextField {
override func becomeFirstResponder() -> Bool {
guard super.becomeFirstResponder() else {
return false
}
if let editor = self.currentEditor() {
editor.perform(#selector(selectAll(_:)), with: self, afterDelay: 0)
}
return true
}
}
Related
I have a NSTextField inside of a NSTableCellView, and I want an event which informs me when my NSTextField has got the focus for disabling several buttons, I found this method:
-(void)controlTextDidBeginEditing:(NSNotification *)obj{
NSTextField *textField = (NSTextField *)[obj object];
if (textField != _nombreDelPaqueteTextField) {
[_nuevaCuentaActivoButton setEnabled:FALSE];
[_nuevaCuentaPasivoButton setEnabled:FALSE];
[_nuevaCuentaIngresosButton setEnabled:FALSE];
[_nuevaCuentaEgresosButton setEnabled:FALSE];
}
}
but it triggers just when my textfield is begin editing as this says, I want the buttons disabled when I get the focus on the textField, not when I already started to type
EDIT: Gonna put my code based on the help received by Joshua Nozzi, it still doesn't work
MyNSTextField.h
#import <Cocoa/Cocoa.h>
#class MyNSTextField;
#protocol MyNSTextFieldDelegate
#optional -(BOOL)textFieldDidResignFirstResponder:(NSTextField *)sender;
#optional -(BOOL)textFieldDidBecomeFirstResponder:(NSTextField *)sender;
#end
#interface MyNSTextField : NSTextField
#property (strong, nonatomic) id <MyNSTextFieldDelegate> cellView;
#end
MyNSTextField.m
#import "MyNSTextField.h"
#implementation MyNSTextField
- (BOOL)becomeFirstResponder
{
BOOL status = [super becomeFirstResponder];
if (status)
[self.cellView textFieldDidBecomeFirstResponder:self];
return status;
}
- (BOOL)resignFirstResponder
{
BOOL status = [super resignFirstResponder];
if (status)
[self.cellView textFieldDidResignFirstResponder:self];
return status;
}
#end
on my viewcontroller EdicionDeCuentasWC.m
#import "MyNSTextField.h"
#interface EdicionDeCuentasWC ()<NSTableViewDataSource, NSTableViewDelegate, NSControlTextEditingDelegate, NSPopoverDelegate, MyNSTextFieldDelegate>
#end
#implementation EdicionDeCuentasWC
#pragma mark MyNSTextFieldDelegate
-(BOOL)textFieldDidBecomeFirstResponder:(NSTextField *)sender{
NSLog(#"textFieldDidBecomeFirstResponder");
return TRUE;
}
-(BOOL)textFieldDidResignFirstResponder:(NSTextField *)sender{
NSLog(#"textFieldDidResignFirstResponder");
return TRUE;
}
#pragma mark --
#end
it's important to say in visual editor, already changed all my NSTextFields to MyNSTextField class and set delegate to my File's Owner (EdicionDeCuentasWC)
I think I nailed it. I was trying subclassing NSTextFiled to override becomeFirstResponder() and resignFirstResponder(), but once I click it, becomeFirstResponder() gets called and resignFirstResponder() gets called right after that. Huh? But search field looks like still under editing and focus is still on it.
I figured out that, when you clicked on search field, search field become first responder once, but NSText will be prepared sometime somewhere later, and the focus will be moved to the NSText.
I found out that when NSText is prepared, it is set to self.currentEditor() . The problem is that when becomeFirstResponder()'s call, self.currentEditor() hasn't set yet. So becomeFirstResponder() is not the method to detect it's focus.
On the other hand, when focus is moved to NSText, text field's resignFirstResponder() is called, and you know what? self.currentEditor() has set. So, this is the moment to tell it's delegate that that text field got focused.
Then next, how to detect when search field lost it's focus. Again, it's about NSText. Then you need to listen to NSText delegate's methods like textDidEndEditing(), and make sure you let it's super class to handle the method and see if self.currentEditor() is nullified. If it is the case, NSText lost it's focus and tell text field's delegate about it.
I provide a code, actually NSSearchField subclass to do the same thing. And the same principle should work for NSTextField as well.
protocol ZSearchFieldDelegate: NSTextFieldDelegate {
func searchFieldDidBecomeFirstResponder(textField: ZSearchField)
func searchFieldDidResignFirstResponder(textField: ZSearchField)
}
class ZSearchField: NSSearchField, NSTextDelegate {
var expectingCurrentEditor: Bool = false
// When you clicked on serach field, it will get becomeFirstResponder(),
// and preparing NSText and focus will be taken by the NSText.
// Problem is that self.currentEditor() hasn't been ready yet here.
// So we have to wait resignFirstResponder() to get call and make sure
// self.currentEditor() is ready.
override func becomeFirstResponder() -> Bool {
let status = super.becomeFirstResponder()
if let _ = self.delegate as? ZSearchFieldDelegate where status == true {
expectingCurrentEditor = true
}
return status
}
// It is pretty strange to detect search field get focused in resignFirstResponder()
// method. But otherwise, it is hard to tell if self.currentEditor() is available.
// Once self.currentEditor() is there, that means the focus is moved from
// serach feild to NSText. So, tell it's delegate that the search field got focused.
override func resignFirstResponder() -> Bool {
let status = super.resignFirstResponder()
if let delegate = self.delegate as? ZSearchFieldDelegate where status == true {
if let _ = self.currentEditor() where expectingCurrentEditor {
delegate.searchFieldDidBecomeFirstResponder(self)
// currentEditor.delegate = self
}
}
self.expectingCurrentEditor = false
return status
}
// This method detect whether NSText lost it's focus or not. Make sure
// self.currentEditor() is nil, then that means the search field lost its focus,
// and tell it's delegate that the search field lost its focus.
override func textDidEndEditing(notification: NSNotification) {
super.textDidEndEditing(notification)
if let delegate = self.delegate as? ZSearchFieldDelegate {
if self.currentEditor() == nil {
delegate.searchFieldDidResignFirstResponder(self)
}
}
}
}
You will need to change NSSerachField to ZSearchField, and your client class must conform to ZSearchFieldDelegate not NSTextFieldDelegate. Here is a example. When user clicked on search field, it extend it's width and when you click on the other place, search field lost it's focus and shrink its width, by changing the value of NSLayoutConstraint set by Interface Builder.
class MyViewController: NSViewController, ZSearchFieldDelegate {
// [snip]
#IBOutlet weak var searchFieldWidthConstraint: NSLayoutConstraint!
func searchFieldDidBecomeFirstResponder(textField: ZSearchField) {
self.searchFieldWidthConstraint.constant = 300
self.view.layoutSubtreeIfNeeded()
}
func searchFieldDidResignFirstResponder(textField: ZSearchField) {
self.searchFieldWidthConstraint.constant = 100
self.view.layoutSubtreeIfNeeded()
}
}
It might depend on the behavior of the OS, I tried on El Capitan 10.11.4, and it worked.
The code can be copied from Gist as well.
https://gist.github.com/codelynx/aa7a41f5fd8069a3cfa2
I have a custom NSTextField subclass that overrides -becomeFirstResponder and -resignFirstResponder. Its -cellView property requires conformance to a protocol that declares -textDidBecome/ResignFirstResponder:(NSTextField *)sender but it's enough to give you the general idea. It can easily be modified to post notifications for which your controller can register as an observer. I hope this helps.
- (BOOL)becomeFirstResponder
{
BOOL status = [super becomeFirstResponder];
if (status)
[self.cellView textFieldDidBecomeFirstResponder:self];
return status;
}
- (BOOL)resignFirstResponder
{
BOOL status = [super resignFirstResponder];
if (status)
[self.cellView textFieldDidResignFirstResponder:self];
return status;
}
I found the following code on the macrumors forums.
Is the first responder a text view (the field editor is a text view).
Does the field editor exist?
Is the text field the field editor's delegate
It seems to work.
- (BOOL)isTextFieldInFocus:(NSTextField *)textField
{
BOOL inFocus = NO;
inFocus = ([[[textField window] firstResponder] isKindOfClass:[NSTextView class]]
&& [[textField window] fieldEditor:NO forObject:nil]!=nil
&& [textField isEqualTo:(id)[(NSTextView *)[[textField window] firstResponder]delegate]]);
return inFocus;
}
Just in case, as a slight variation over the idea of #sam, we can observe NSWindow.firstResponder property itself, it's KVO-compliant according to the documentation. Then compare it with textField or textField.currentEditor() to figure out whether the field is focused.
I’m trying to replicate the behaviour of the search field in iTunes, for looking up stock symbols and names. Specifically, as you start typing in the search field a popover appears with the filtered items. For the most part I have this working however what I can’t replicate is the way it handles first responder
I have my popover appear after three characters are entered. At this point the NSSearchField would lose first responder status and therefore I could no longer continue typing. The behaviour I would like is
the ability to continue typing after the popover appears
if scrolling through the items with the arrow keys, and then resume typing, you would continue from the last character in the Search field.
What I tried is subclassing NSTextView (use this as the custom field editor for the NSSearchField) and overriding
- (BOOL)resignFirstResponder
By simply returning NO, I can continue typing once the popover appears, but obviously I can’t select any of the items in the popover. So i tried the following, which returns YES if the down arrow or a mousedown event occurs.
#interface SBCustomFieldEditor ()
{
BOOL resignFirstRepond;
}
#end
#implementation SBCustomFieldEditor
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
resignFirstRepond = NO;
}
return self;
}
- (BOOL)resignFirstResponder
{
return resignFirstRepond;
}
- (void)keyDown:(NSEvent *)theEvent
{
if ([theEvent keyCode] == 125) {
resignFirstRepond = YES;
[self resignFirstResponder];
}
[super keyDown:theEvent];
}
- (void)mouseDown:(NSEvent *)theEvent
{
resignFirstRepond = YES;
[self resignFirstResponder];
}
This works for the mousedown event, but not the keydown event, furthermore this doesn’t address the issue, when the user resumes typing.
Any suggestions?
In the meantime I found an easy fix. Subclass your text view and implement - (BOOL)canBecomeKeyView. Always return NO there. It will be called only once when the popover is shown. You can work with the text view any time still.
I have a cell-based NSOutlineView which displays NSTextFieldCell objects.
I'd like to respond to keydown or keyup events so as to make the text contained in the NSTextFieldCell bold when the text contains certain preset keywords. What is the most elegant way to achieve this - should I:
Subclass NSOutlineView and override the keydown method
Subclass NSTextFieldCell
Utilize a delegate of some kind
Utilize some other approach
Thanks very much in advance to all for any info!
Found it.
In awakeFromNib:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(actionToTakeOnKeyPress:) name:NSControlTextDidChangeNotification object:theNSOutlineViewThatContainsTheNSTextFieldCell];
Then add a method like this:
- (void) actionToTakeOnKeyPress: (id) sender
{
//will be called whenever contents of NSTextFieldCell change
}
To intercept key presses in a way that they can still be filtered out, various NSResponder messages may be overwritten, such as keyDown: or interpretKeyEvents:.
To be able to do that, a subclass of a NSTextView needs to be used as the field editor. For that, one subclasses NSTextFieldCell and overrides fieldEditorForView:, returning the subclass (see Custom field editor for NSTextFieldCell in an NSTableView).
Here's the relevant code excerpts:
In a subclassed NSTextFieldCell (which then has to be assigned in Interface Builder for the editable column, or returned by the NSTableViewDelegate's dataCellForTableColumn message):
- (NSTextView *)fieldEditorForView:(NSView *)aControlView
{
if (!self.myFieldEditor) {
self.myFieldEditor = [[MyTextView alloc] init];
self.myFieldEditor.fieldEditor = YES;
}
return self.myFieldEditor;
}
It also requires the declaration of a property in the #interface section:
#property (strong) MyTextView *myFieldEditor;
And then in MyTextView, which is a subclass of NSTextView:
-(void)keyDown:(NSEvent *)theEvent
{
NSLog(#"MyTextView keyDown: %#", theEvent.characters);
static bool b = true;
if (b) { // this silly example only lets every other keypress through.
[super keyDown:theEvent];
}
b = !b;
}
I have one NSTextView within NSBox. I want to draw focus ring aroung NSBox whenever NSTextView got focus and remove focus ring as soon as NSTextView lost focus.
Thanks,
To do this, create a subclass of NSTextView, and override the -becomeFirstResponder like so:
- (BOOL)becomeFirstResponder
{
BOOL returnValue = [super becomeFirstResponder];
if (returnValue) {
//do something here when this becomes first responder
}
return returnValue;
}
You can set up an NSNotification in the if statement above so that when that code block gets run, your view containing the NSBox can get called and subsequently draw a focus ring on the NSBox. To handle the NSTextView losing focus, you'll want to override -resignFirstResponder, like so:
- (BOOL)resignFirstResponder
{
BOOL returnValue = [super resignFirstResponder];
if(returnValue){
//do something when resigns first responder
}
return returnValue;
}
Be sure to change the class in interface builder, and change your class type in your header and/or implementation files to your new subclass of NSTextView:
I have a view overlayed on top of many other views. I am only using the overaly to detect some number of touches on the screen, but other than that I don't want the view to stop the behavior of other views underneath, which are scrollviews, etc. How can I forward all the touches through this overlay view? It is a subclass of UIView.
Disabling user interaction was all I needed!
Objective-C:
myWebView.userInteractionEnabled = NO;
Swift:
myWebView.isUserInteractionEnabled = false
For passing touches from an overlay view to the views underneath, implement the following method in the UIView:
Objective-C:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(#"Passing all touches to the next view (if any), in the view stack.");
return NO;
}
Swift 5:
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
print("Passing all touches to the next view (if any), in the view stack.")
return false
}
This is an old thread, but it came up on a search, so I thought I'd add my 2c. I have a covering UIView with subviews, and only want to intercept the touches that hit one of the subviews, so I modified PixelCloudSt's answer to:
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
for (UIView* subview in self.subviews ) {
if ( [subview hitTest:[self convertPoint:point toView:subview] withEvent:event] != nil ) {
return YES;
}
}
return NO;
}
Improved version of #fresidue answer. You can use this UIView subclass as transparent view passing touches outside its subview. Implementation in Objective-C:
#interface PassthroughView : UIView
#end
#implementation PassthroughView
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
for (UIView *view in self.subviews) {
if (!view.hidden && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
return YES;
}
}
return NO;
}
#end
.
and in Swift:
class PassthroughView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return subviews.contains(where: {
!$0.isHidden
&& $0.isUserInteractionEnabled
&& $0.point(inside: self.convert(point, to: $0), with: event)
})
}
}
TIP:
Say then you have a large "holder" panel, perhaps with a table view behind. You make the "holder" panel PassthroughView. It will now work, you can scroll the table "through" the "holder".
But!
On top of the "holder" panel you have some labels or icons. Don't forget, of course those must simply be marked user interaction enabled OFF!
On top of the "holder" panel you have some buttons. Don't forget, of course those must simply be marked user interaction enabled ON!
Note that somewhat confusingly, the "holder" itself - the view you use PassthroughView on - must be marked user interaction enabled ON! That's ON!! (Otherwise, the code in PassthroughView simply will never be called.)
I needed to pass touches through a UIStackView. A UIView inside was transparent, but the UIStackView consumed all touches. This worked for me:
class PassThrouStackView: UIStackView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let view = super.hitTest(point, with: event)
if view == self {
return nil
}
return view
}
}
All arrangedSubviews still receive touches, but touches on the UIStackView itself went through to the view below (for me a mapView).
I had a similar issue with a UIStackView (but could be any other view).
My configuration was the following:
It's a classical case where I have a container that needed to be placed in the background, with buttons on the side. For layout purposes, I included the buttons in a UIStackView, but now the middle (empty) part of the stackView intercepts touches :-(
What I did is create a subclass of UIStackView with a property defining the subView that should be touchable.
Now, any touch on the side buttons (included in the * viewsWithActiveTouch* array) will be given to the buttons, while any touch on the stackview anywhere else than these views won't be intercepted, and therefore passed to whatever is below the stack view.
/** Subclass of UIStackView that does not accept touches, except for specific subviews given in the viewsWithActiveTouch array */
class NoTouchStackView: UIStackView {
var viewsWithActiveTouch: [UIView]?
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if let activeViews = viewsWithActiveTouch {
for view in activeViews {
if CGRectContainsPoint(view.frame, point) {
return view
}
}
}
return nil
}
}
If the view you want to forward the touches to doesn't happen to be a subview / superview, you can set up a custom property in your UIView subclass like so:
#interface SomeViewSubclass : UIView {
id forwardableTouchee;
}
#property (retain) id forwardableTouchee;
Make sure to synthesize it in your .m:
#synthesize forwardableTouchee;
And then include the following in any of your UIResponder methods such as:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.forwardableTouchee touchesBegan:touches withEvent:event];
}
Wherever you instantiate your UIView, set the forwardableTouchee property to whatever view you'd like the events to be forwarded to:
SomeViewSubclass *view = [[[SomeViewSubclass alloc] initWithFrame:someRect] autorelease];
view.forwardableTouchee = someOtherView;
In Swift 5
class ThroughView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
guard let slideView = subviews.first else {
return false
}
return slideView.hitTest(convert(point, to: slideView), with: event) != nil
}
}
Looks like even thou its quite a lot of answers here, there is no one clean in swift that I needed.
So I took answer from #fresidue here and converted it to swift as it's what now mostly developers want to use here.
It solved my problem where I have some transparent toolbar with button but I want toolbar to be invisible to user and touch events should go through.
isUserInteractionEnabled = false as some stated is not an option based on my testing.
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
for subview in subviews {
if subview.hitTest(convert(point, to: subview), with: event) != nil {
return true
}
}
return false
}
I had couple of labels inside StackView and I didn't have much success with the solutions above, instead I solved my problem using below code:
let item = ResponsiveLabel()
// Configure label
stackView.addArrangedSubview(item)
Subclassing UIStackView:
class PassThrouStackView:UIStackView{
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
for subview in self.arrangedSubviews {
let convertedPoint = convert(point, to: subview)
let labelPoint = subview.point(inside: convertedPoint, with: event)
if (labelPoint){
return subview
}
}
return nil
}
}
Then you could do something like:
class ResponsiveLabel:UILabel{
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Respond to touch
}
}
Try something like this...
for (UIView *view in subviews)
[view touchesBegan:touches withEvent:event];
The code above, in your touchesBegan method for example would pass the touches to all of the subviews of view.
The situation I was trying to do was build a control panel using controls inside nested UIStackView’s. Some of the controls had UITextField’s others with UIButton’s. Also, there were labels to identify the controls. What I wanted to do was put a big “invisible” button behind the control panel so that if a user tapped on an area outside a button or text field, that I could then catch that and take action - primarily dismiss any keyboard if a text field was active (resignFirstResponder). However, tapping on a label or other blank area in the control panel would not pass things through. The above discussions were helpful in coming up with my answer below.
Basically, I sub-classed UIStackView and overwrote the “point(inside:with) routine to look for the type of controls that needed the touch and “ignore” things like labels that I wanted to ignore. It also checks for inside UIStackView’s so that things can recurse into the control panel structure.
The code is a perhaps a little more verbose than it should be. But it was helpful in debugging and hopefully provides more clarity in what the routine is doing. Just be sure in Interface Builder to change the class of the UIStackView's to PassThruStack.
class PassThruStack: UIStackView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
for view in self.subviews {
if !view.isHidden {
let isStack = view is UIStackView
let isButton = view is UIButton
let isText = view is UITextField
if isStack || isButton || isText {
let pointInside = view.point(inside: self.convert(point, to: view), with: event)
if pointInside {
return true
}
}
}
}
return false
}
}
As suggested by #PixelCloudStv if you want to throw touched from one view to another but with some additional control over this process - subclass UIView
//header
#interface TouchView : UIView
#property (assign, nonatomic) CGRect activeRect;
#end
//implementation
#import "TouchView.h"
#implementation TouchView
#pragma mark - Ovverride
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL moveTouch = YES;
if (CGRectContainsPoint(self.activeRect, point)) {
moveTouch = NO;
}
return moveTouch;
}
#end
After in interfaceBuilder just set class of View to TouchView and set active rect with your rect. Also u can change and implement other logic.