This is what I want the app to do.
Tap in a "Text View" not a "Text Field", then the keyboard is displayed and a bullet point ("\u2022") pops up. You type your sentence, and when you hit return it makes a new bullet.
So essentially, you have a Text View that is a bulleted list. Much like Word, where you start a bullet and when you hit return it make a new bullet for you, so you can start typing on that line.
How do I do this?
This is what worked for any other stupid noobs like me:
I put this into my .m file
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if([text isEqualToString:#"\n"])
{
NSString *modifiedString = [myTextView.text stringByAppendingString:#"\n\u2022"];
[myTextView setText:modifiedString];
return NO;
}
return YES;
}
I put this into my .h file
#interface CRHViewController3 : UIViewController <UITextViewDelegate> {
__weak IBOutlet UITextView *myTextView;
}
and then I put this under my viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
[myTextView setDelegate:self];
}
Try out this.
(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if([text isEqualToString:"\n"])
{
NSString *modifiedString = [textView.text stringByAppendingString:#"\n\u2022"];
[textView setText:modifiedString];
return NO;
}
return YES;
}
The method -(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text would be called for each letter entered on deleted.
So in this method you could try to replace all line breaks with bullet char.
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
...
textView.text = [textView.text stringByReplacingOccurrencesOfString:#"\\n" withString:#"\u2022"];
...
}
To use this code:
(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if([text isEqualToString:"\n"])
{
NSString *modifiedString = [textView.text stringByAppendingString:#"\n\u2022"];
[textView setText:modifiedString];
return NO;
}
return YES;
}
Your header file must look like this:
#interface MyViewController : UIViewController <UITextViewDelegate> {
//instance variables in here
}
Related
The previous version of this question was closed because the moderator did not recognize that this is a cell-based Outline not a view-based outline. The answer the moderator suggested does not work for a cell-based outline.
The question as previously asked was:
I have a cell-based NSOutlineView. How do I get the contents of the NSTextFieldCell when the user Quits the app while editing that cell. Currently, attributedStringValue returns the contents before editing began.
As requested in the comments, here is a NSViewController.h and .m . It references a storyboard with two outlets: outlineView and cellOutlet as shown in viewController.h.
ViewController.h
#import <Cocoa/Cocoa.h>
#interface ViewController : NSViewController <NSOutlineViewDataSource,NSOutlineViewDelegate, NSTextStorageDelegate, NSTextViewDelegate, NSWindowDelegate, NSTextFinderClient,NSTextFieldDelegate,NSSearchFieldDelegate>
#property (weak) IBOutlet NSOutlineView *outlineView;
#property (weak) IBOutlet NSTextFieldCell *cellOutlet;
#property(nonatomic,strong)NSMutableAttributedString* string;
#end
and ViewController.m
#import "ViewController.h"
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
-(void)viewWillAppear
{
self.outlineView.dataSource = self;
self.outlineView.delegate = self;
self.outlineView.window.delegate = self;
self.string = [[NSMutableAttributedString alloc] initWithString:#"TEST"];
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
}
-(BOOL)windowShouldClose:(NSWindow *)sender
{
[self.view.window makeFirstResponder:nil];
BOOL response = [sender makeFirstResponder:sender];
NSMutableAttributedString* changedText = [[_cellOutlet attributedStringValue] mutableCopy];
NSLog(#"On Quit value was: %#", changedText);
return response;
}
-(id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
NSMutableAttributedString* string = [[NSMutableAttributedString alloc] initWithString:#"Outline Item"];
return string;
}
-(BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
return NO;
}
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
{
return _string;
}
}
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
if (!item)
{
return 1;
}
else
{
return 0;
}
}
- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
_string = object;
NSLog(#"Changed Value: %#",_string);
}
#end
Implement windowShouldClose in the window delegate class and call [window makeFirstResponder:nil].
- (BOOL)windowShouldClose:(NSWindow *)sender {
return [sender makeFirstResponder:nil];
}
Implement applicationShouldTerminate in the application delegate class and test if the key window should close.
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
NSWindow *window = [sender keyWindow];
if (![window.delegate windowShouldClose:window])
return NSTerminateCancel;
return NSTerminateNow;
}
Set "Application can be killed immediately when user is shutting down or logging out" to NO in info.plist.
I currently developing a small dictionary app under OSX for my own use, I would like to have a feature that when I hit the return key, the focus would go to the nssearchfeild.
So I try to make the app to receive keyDown event using a NSView and NSViewController told by this tutorial.
But every time I start the app, it wouldn't receive the keyDown event. I have to click on the window once, then hit the keyboard, so that it can receive keyDown event.
What did I do wrong? Can anyone help me out with this problem? I have been stuck in this problem for days, and searching throught Google and API wouldn't help much.
Thanks in advance!
Here is my code for AppDelegate.m
#import "AppDelegate.h"
#import "MyDictViewController.h"
#interface AppDelegate()
#property (nonatomic,strong) IBOutlet MyDictViewController *viewController;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
self.viewController = [[MyDictViewController alloc] initWithNibName:#"MyDictViewController" bundle:nil];
[self.window.contentView addSubview:self.viewController.view];
self.viewController.view.frame = ((NSView*)self.window.contentView).bounds;
[self.window makeKeyAndOrderFront:nil];
}
#end
And My ViewController.m
#import "MyDictViewController.h"
#import "FileHelper.h"
#import <Carbon/Carbon.h>
#interface MyDictViewController ()
#property (weak) IBOutlet NSTableView *wordsFilteredTable;
#end
#implementation MyDictViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.IMAGE_FILE = [NSImage imageNamed:#"Document.png"];
self.wordlist = [FileHelper readLines];
self.filterWordlist = [[NSMutableArray alloc] init];
}
return self;
}
- (void)loadView
{
[super loadView];
[self.view becomeFirstResponder];
}
-(void)keyDown:(NSEvent*)theEvent
{
NSLog(#"Caught key event");
}
-(void)keyUp:(NSEvent *)theEvent
{
unsigned short keycode = [theEvent keyCode];
switch (keycode)
{
case kVK_Return:
[self.searchField becomeFirstResponder];
default:
break;
}
}
-(void)mouseDown:(NSEvent*)theEvent
{
NSLog(#"Caught mouse event");
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
self.wordsFilteredTable.rowHeight = 37;
NSTableCellView *cellView = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
if( [tableColumn.identifier isEqualToString:#"WordColumn"] )
{
NSString *word = [self.filterWordlist objectAtIndex:row];
cellView.textField.stringValue = word;
cellView.imageView.image = self.IMAGE_FILE;
return cellView;
}
return cellView;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return [self.filterWordlist count];
}
- (void)controlTextDidChange:(NSNotification *)obj
{
NSTextView* textView = [[obj userInfo] objectForKey:#"NSFieldEditor"];
self.currentWord = [textView string];
[self.filterWordlist removeAllObjects];
for(NSString* word in self.wordlist) {
if ([word hasPrefix:self.currentWord]) {
[self.filterWordlist addObject:word];
}
}
[self.wordsFilteredTable reloadData];
}
#end
And my AppView.m
#import "AppView.h"
#implementation AppView
- (void)setViewController:(NSViewController *)newController
{
if (viewController)
{
[super setNextResponder:[viewController nextResponder]];
[viewController setNextResponder:nil];
}
viewController = newController;
if (newController)
{
[super setNextResponder: viewController];
[viewController setNextResponder:[self nextResponder]];
}
}
- (void)setNextResponder:(NSResponder *)newNextResponder
{
if (viewController)
{
[viewController setNextResponder:newNextResponder];
return;
}
[super setNextResponder:newNextResponder];
}
#end
I write a simple application with Xcode 5 on iPhone iOS7 device.
I have a label that increments by +/- Buttons, but i want to give option for user to insert his number to this label.
How can i do it with long press recogniser?
Thanks.
Use a UILongPressGestureRecognizer and a UITextView.
Add a UILongPressGstureRecognizer property to your view controller:
#property UILongPressGestureRecognizer *gestureRecognizer;
You need to declare that your view controller conforms to the UITextViewDelegate and UIGestureRecognizerDelegate protocols:
#interface ViewController : UIViewController<UITextViewDelegate, UIGestureRecognizerDelegate>
In viewDidLoad:
self.textView.editable = NO;
self.textView.delegate = self;
self.gestureRecognizer = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(textViewLongPressed:)];
self.gestureRecognizer.delegate = self;
[self.textView addGestureRecognizer:self.gr];
This is the method that will be called when you long press the text view:
-(void) textViewLongPressed:(UILongPressGestureRecognizer *)sender
{
self.textView.editable = YES;
[self.textView becomeFirstResponder];
}
Implement this method from the UIGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if (self.gestureRecognizer == gestureRecognizer){
return YES;
}
return NO;
}
When you finish editing the text view
-(void) textViewDidEndEditing:(UITextView *)textView
{
self.textView.editable = NO;
}
To dismiss the keyboard when you press return:
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if ([text isEqualToString:#"\n"])
[textView resignFirstResponder]; // or [textView endEditing:YES]
return YES;
}
I would like a text similar to Twitter's
I am currently using a UITextView since it has multi-line support. I kind of figured out how to use a label that counts the characters and keeps the user up to date of any characters added or removed.
Empty:
With Text:
The problem I am having however is when I copy or paste any text in the view, (like if I highlight all the text, as in the second picture) and I delete it, the counter will not update. Or if I paste any text, the count just decrements one character, though the text field limit still stays correct, just the counter is no longer in sync with it.
Code: http://pastebin.com/UK91Ccbb
There is a delegate method for UITextView as
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
it is called every time a character is inserted into the UITextView. In this method set the length of text in your counter UILabel as follows
counterlabel.text = [NSString stringWithFormat:#"%i", textView.text.length];
return YES at the end of method if the length is within range i.e. less than or equal to 140 else return NO.
UPDATE:..
To restrict to 140 characters.
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
return textView.text.length <= 140;
}
To handle text removal on selection and counting characters.
- (void)textViewDidChange:(UITextView *)textView
{
count.text = [NSString stringWithFormat:#"%i", (140- textView.text.length)];
}
NSLog(#"You added a character");
charactersLeft--;
I think the problem here is that you should have written:
charactersLeft -= text.length;
In viewDidLoad :
charactersLeft = 140;
characterCountLabel.text = [NSString stringWithFormat:#"%d",charactersLeft];
Now use this it works well :
- (void)textViewDidChange:(UITextView *)textView{
NSString *textString = textView.text;
int chars = [textString length];
if (chars <= 140) {
[characterCountLabel setText:[NSString stringWithFormat:#"%d",(140 - chars)]];
}
}
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if([text isEqualToString:#"\n"])
[textView resignFirstResponder];
if (textView.text.length == 140 && [text isEqualToString:#"\n"]) {
[textView resignFirstResponder];
}
else if ((textView.text.length) >= 140 && range.length == 0){
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"" message:#"You have exceeded the characters limit." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
return NO; // return NO to not change text
}
return YES;
}
Note that here I have used returnType key to resign the keyboard.You may change it to work as usual.
I've been working through the exercises in a book recommended here on stackoverflow, however I've run into a problem and after three days of banging my head on the wall, I think I need some help.
I'm working through the "Speakline" exercise where we add a TableView to the interface and the table will display the "voices" that you can choose for the text to speech aspect of the program.
I am having two problems that I can't seem to get to the bottom of:
I get the following error: *** Illegal NSTableView data source (). Must implement numberOfRowsInTableView: and tableView:objectValueForTableColumn:row:
The tableView that is supposed to display the voices comes up blank
I have a feeling that both of these problems are related.
I'm including my interface code here:
#import <Cocoa/Cocoa.h>
#interface AppController : NSObject <NSSpeechSynthesizerDelegate, NSTableViewDelegate>
{
IBOutlet NSTextField *textField;
NSSpeechSynthesizer *speechSynth;
IBOutlet NSButton *stopButton;
IBOutlet NSButton *startButton;
IBOutlet NSTableView *tableView;
NSArray *voiceList;
}
- (IBAction)sayIt:(id)sender;
- (IBAction)stopIt:(id)sender;
#end
And my implementation code here:
#import "AppController.h"
#implementation AppController
- (id)init
{
[super init];
//Log to help me understand what is happening
NSLog(#"init");
speechSynth = [[NSSpeechSynthesizer alloc] initWithVoice:nil];
[speechSynth setDelegate:self];
voiceList = [[NSSpeechSynthesizer availableVoices] retain];
return self;
}
- (IBAction)sayIt:(id)sender
{
NSString *string = [[textField stringValue] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
//Is the string zero-length?
if([string length] == 0) {
NSLog(#"String from %# is a string with a length of %d.", textField, [string length]);
[speechSynth startSpeakingString:#"Please enter a phrase first."];
}
[speechSynth startSpeakingString:string];
NSLog(#"Started to say: %#", string);
[stopButton setEnabled:YES];
[startButton setEnabled:NO];
}
- (IBAction)stopIt:(id)sender
{
NSLog(#"Stopping...");
[speechSynth stopSpeaking];
}
- (void) speechSynthesizer:(NSSpeechSynthesizer *)sender didFinishSpeaking:(BOOL)complete
{
NSLog(#"Complete = %d", complete);
[stopButton setEnabled:NO];
[startButton setEnabled:YES];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
return [voiceList count];
}
- (id)tableView: (NSTableView *)tv objecValueForTableColumn: (NSTableColumn *)tableColumn
row:(NSInteger)row
{
NSString *v = [voiceList objectAtIndex:row];
NSLog(#"v = %#",v);
NSDictionary *dict = [NSSpeechSynthesizer attributesForVoice:v];
return [dict objectForKey:NSVoiceName];
}
/*
- (BOOL)respondsToSelector:(SEL)aSelector
{
NSString *methodName = NSStringFromSelector(aSelector);
NSLog(#"respondsToSelector: %#", methodName);
return [super respondsToSelector:aSelector];
}
*/
#end
Hopefully, you guys can see something obvious that I've missed.
Thank you!
objecValueForTableColumn is not the same as objectValueForTableColumn. When it comes to delegates and data sources, I recommend never typing the method names if you can avoid it - it causes exactly this kind of problem. If you copy & paste the method signature out of the documentation you can be safer. Good luck with your learning!