NSTextFinder set search string and clear visual feedback programmatically - objective-c

I have an NSTextView that uses the find bar ([textView setUsesFindBar:YES];).
I have 2 questions.
How do I clear the visual feedback from a find operation?
My problem happens when I programmatically change the content of the textView. The visual feedback for a search operation on the previous content remains after the content change. Obviously these yellow boxes do not apply to the new content so I need a way to clear them when changing the textView content.
Note: I did not implement the NSTextFinderClient protocol because I have a simple textView and the find bar just works without any other effort.
How can I send a search string to the find bar?

I found my answers, so for others here's how to do it.
First you need an instance of NSTextFinder so you can control it. We set that up in code.
textFinder = [[NSTextFinder alloc] init];
[textFinder setClient:textView];
[textFinder setFindBarContainer:[textView enclosingScrollView]];
[textView setUsesFindBar:YES];
[textView setIncrementalSearchingEnabled:YES];
First answer: To clear visual feedback I can do either of 2 things. I can just cancel the visual feedback...
[textFinder cancelFindIndicator];
Or I can alert NSTextFinder that I'm about to change my textView content...
[textFinder noteClientStringWillChange];
Second answer: There's a global NSFindPboard. You can use that to set a search.
// change the NSFindPboard NSPasteboardTypeString
NSPasteboard* pBoard = [NSPasteboard pasteboardWithName:NSFindPboard];
[pBoard declareTypes:[NSArray arrayWithObjects:NSPasteboardTypeString, NSPasteboardTypeTextFinderOptions, nil] owner:nil];
[pBoard setString:#"new search" forType:NSStringPboardType];
NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSTextFinderCaseInsensitiveKey, [NSNumber numberWithInteger:NSTextFinderMatchingTypeContains], NSTextFinderMatchingTypeKey, nil];
[pBoard setPropertyList:options forType:NSPasteboardTypeTextFinderOptions];
// put the new search string in the find bar
[textFinder cancelFindIndicator];
[textFinder performAction:NSTextFinderActionSetSearchString];
[textFinder performAction:NSTextFinderActionShowFindInterface]; // make sure the find bar is showing
There's a problem though. The actual text field in the find bar does not get updated after that code. I found that if I toggle the first responder then I can get it to update...
[myWindow makeFirstResponder:outlineView];
[myWindow makeFirstResponder:textView];

Related

Why is app hanging only on iOS 10 when the clear button is tapped within a UITextField?

I have a situation where users, only on iOS 10 (10.0 thru 10.3 at the time of this post) encounter a complete app hang when trying to clear text from certain UITextFields by tapping the built-in clear button. This problem does not happen in all view controllers, yet all share the exact same delegate implementation. The fields are only used for US monetary input (digits and decimal) and are constrained by delegate to a certain number of characters.
I have reproduced this hang in the simulator and painstakingly confirmed it does not reproduce on iOS 9.3.5 or earlier, only iOS 10.0 and later. I have narrowed down that this problem has to do with setting attributed text set within a UITextField delegate. Again it only reproduces with some view controllers in the app, not all. But once a repro situation is identified, the above iOS version test results hold true.
When pressing the clear button, only after once having gone thru the editing done delegate path, the app goes into a complete runaway loop somewhere within the iOS SDK itself. I can reproduce it with the below example delegate alone:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// apply our desired text attributes
//
// ensure right justification within the field
NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.alignment = NSTextAlignmentRight;
// set desired fontface, fontsize, color, and field alignment
NSMutableAttributedString *attributedString = [textField.attributedText mutableCopy];
[attributedString addAttribute:NSFontAttributeName value:[UIFont fontWithName:#"Bradley Hand" size:17.0f] range:NSMakeRange(0, [attributedString length])];
[attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor darkGrayColor] range:NSMakeRange(0, [attributedString length])];
[attributedString addAttribute:NSParagraphStyleAttributeName value:paragraph range:NSMakeRange(0, [attributedString length])];
// apply the changes in font, size, and color to be visible
textField.attributedText = attributedString;
return YES;
}
The oddest thing is that this does not happen in all UITextFields using this same delegate implementation, only some view controllers are affected. Others with this same code work just fine!
This implies there are some additional conditions for UIKit to get into this situation that I am unable to identify just yet.
After finding this workaround I must conconclude this is a bug in the iOS 10 SDK, related in some way to other interactions performed by some view controllers within my app.
In reading about a UITextField hang for an unrelated scenario, I tried the workaround identified in this post TextField input hangs inputting too many characters
Changing the clear delegate to set the property mentioned also works around this clearing of attributed text bug scenario I have encountered.
- (BOOL)textFieldShouldClear:(UITextField *)textField {
// work around iOS 10 bug
textField.adjustsFontSizeToFitWidth = NO;
return YES;
}
I am not sure why this has a positive impact on the problem, as the text entry in this apps' fields is delegate constrained to no more than 5 characters always and the width of these fields in storyboard is 6X of that.
I post this as my own answer as a verified workaround, but am looking for other answers that may avoid the situation being encountered altogether.

Click on UILabel and perform action

I have a simple problem, I have a string like "#my#name#is#umesh#verma" and assign to a UITableview cell label,
cell.detaillabel.text = #"#my#name#is#umesh#verma";
My Problem is how to get each word name when I click on single item.
If I click on #umesh then I get "umesh" word..
More better solution is add custom label which supports touches. For example TTTAttributedLabel supports touches on links.
Main task is get notification when user touch a word and to identify the word.
You can add URLs (with special format) to any word and subscribe to a notification when user click it (as delegate to the label). For example you can create this URL for "word":
touchscheme://word
I haven't checked about how to perform an action when clicking on a UILabel. However, I experienced that with UITextView. You can use "NSLinkAttributeName" to do that.
Basically, from your original string, try to find the range of string that you need to trigger actions. Then add a link value.
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]initWithString:yourString];
[attributedString addAttribute:NSLinkAttributeName value:url range:range];
[textView setAttributedText:attributedString];
Set delegate to your textView and handle the following method
-(BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange{
// You can retrive your string here or perform an action
return YES;
}
Hope this would be helpful for you.

iOS - Only first line of text is viewable

In two places in our app the text that the user types in only shows first line of text. Both occurances are in external frameworks, first in UIActivityView, the other in Freshdesk MobiHelp.
First, with UIActivityView, when using Twitter:
The problem is that if the text goes beyond one row in the modal, the text goes transparent:
NSString *textToShare = [NSString stringWithFormat:NSLocalizedString(#"CHALLENGE-TWITTER- DEFAULT-TEXT", nil), [UserManager currentUser].displayName];
NSString *urlToShare = [NSURL URLWithString:#"http://example.com"];
NSArray *activityItems = #[textToShare, urlToShare];
UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
[self presentViewController:activityVC animated:YES completion:nil];
Second, in Freshdesk submit a ticket:
I should also add that the Facebook modal from UIActivityView works just fine:
Would really appreciate any tips here, as I'm lost.
I think in the second sreenshot that you have attached here, there is a white screen over a red area which probably might be your label or the text area and I guess its blocking your text area. If the view was added by you, you can get the particular text area to front so that its not blocked and you can see your text. Hope this helps
Can you see a white box inside the one I highlighted? There is some view over your textarea. How are you adding your textarea? As a subview?? Can you just get log the main views subviews?? Like
NSLog (#"%#",[self.view subviews]);
And now check the view hierarchy.

Enabling UIBarButtonItem in toolbar [duplicate]

I have a UIToolbar that I set up using IB with three buttons, left, middle and right. In some situations I would like to not display the middle button. Does anybody know of a way to hide a specific button on inside a UIToolBar? There is no hide property, all I can find is setEnable but this still leaves the button causing users to wonder what its purpose is. I would like to only display it in situations that it actually has a use.
Thanks in advance!
Reset the items:
-(void)setItems:(NSArray *)items animated:(BOOL)animated
You can get the current items using the items property, then just remove the one you don't want to show and pass in the new NSArray.
As you can see, you can also animate it to make it clear to the user.
Rather than guessing at the index, I added an IBOutlet for the UIBarButtonItem and then removed it by name:
NSMutableArray *toolBarButtons = [self._toolbar.items mutableCopy];
[toolBarButtons removeObject:self._selectButton]; // right button
[self._toolbar setItems:toolBarButtons];
And of course it helps to connect the outlets in the designer :)
This is how i did it.. too much headache but its the best i could come up with :
NSArray *toolBarArray = toolBar.items;
NSMutableArray *newToolBarArray = [NSMutableArray arrayWithArray:toolBarArray];
[newToolBarArray removeObjectAtIndex:2];
[newToolBarArray removeObjectAtIndex:1];
//remove whatever buttons you want to.
NSArray *finalTabBarArray =[[NSArray alloc] initWithObjects:newToolBarArray, nil];
[toolBar setItems:[finalTabBarArray objectAtIndex:0] animated:NO];
I know it is quite old thread for but those who look this page for solution, here you go :
With iOS7, you can use this approach to show/hide your toolbar button :
if(// your code Condition)
{ self.toolbarBtn1.enabled = YES;
self.toolbarBtn1.tintColor = nil; }
else
{ self.toolbarBtn1.enabled = NO;
self.toolbarBtn1.tintColor = [UIColor clearColor]; }
This does not work here because the array you are sending with setItem is not what the function expects.
I had to replace the line:
NSArray *finalTabBarArray = [[NSArray alloc] initWithObjects:newToolBarArray, nil];
with this one:
NSArray *finalTabBarArray = [newToolBarArray copy];
Then it works perfectly.
Mohit's answer is one that I have used, but you dont need to specifically make it a NSArray that the toolbar sets. You can just set the array of items as a NSMutableArray. No real advantage that I am aware off but its a few lines less code. And that way you can take the array and move about UIButton objects as you would any other array with objects and then just reset the toolbar with that mutable array.
[newToolBarArray removeObjectAtIndex:2];
[newToolBarArray removeObjectAtIndex:1];
[toolBar setItems:newToolBarArray];

NSTextField - make selected text bold, italic or underline?

I have an NSTextField where the user can write text. I would like to be able to make 3 buttons: bold, italic and underline; these buttons should change the user selection in the textfield to either bold, italic or underline.
Can anyone give me a pointer on how to do this?
The first thing is to enable rich text support, and you can do it either in Interface Builder by checking the "Rich Text" option in the inspector or by code using setAllowsEditingTextAttributes:.
Then it's all about NSAttributedStrings.
The big problem though is that looks like you need to apply changes to the selected text. This is not possible with NSTextFields. Only with NSTextViews.
If you can change it, go ahead and it will make things easier. However, if you do need to stick with NSTextField you may want to access the field editor. Each window has one associated, and it's what process the text behind the scenes.
NSTextView *editor = (NSTextView *)[window fieldEditor:YES forObject:myTextField]
Then you can call NSTextView's method setSelectedTextAttributes: happily.
Read more about the field editor here at Apple and in CocoaDev
Assuming your NSTextfield * is textField, the code below underlines the selection:
NSMutableAttributedString * as = [[[textField attributedStringValue] mutableCopy] autorelease];
[as beginEditing];
[as addAttribute:NSUnderlineStyleAttributeName
value:[NSNumber numberWithInt:NSUnderlineStyleSingle]
range:[[[textField window] fieldEditor:YES forObject:textField] selectedRange]];
[as endEditing];
[textField setAttributedStringValue:as];