6

I've been researching this for a few days now, and would appreciate a little help. Is there any way to generate a multi-line UITextField like Apple use in the SMS application? The useful thing about this control is that it has the 'sunk' appearance that makes it clear that it is a text entry box, but at the same time, it expands on each new-line character.

Failing that, if I'm forced to use a UITextView, can anyone advise how best to dismiss the keyboard ? Both the 'Done' and the 'Go' buttons just appear to generate newline characters ('\n'). This seems wrong to me - surely at least one of these should generate a different character, so that I can still allow for newline characters, but also dismiss my keyboard on a specific key press.

Am I missing something simple here ?

Thanks in advance :)

Thomas Zoechling
  • 34,177
  • 3
  • 81
  • 112
Anonymouslemming
  • 518
  • 2
  • 9
  • 16

5 Answers5

9

Maybe you can build upon a class I wrote? It's the same as tttexteditor, without the ugly glitches: http://www.hanspinckaers.com/multi-line-uitextview-similar-to-sms

HansPinckaers
  • 1,755
  • 15
  • 18
3

An old question, but after several hours I've figured out how to make it the same perfectly as in Instagram (it has the best algorithm among all BTW)

Initialize with this:

    // Input
    _inputBackgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, size.height - _InputBarHeight, size.width, _InputBarHeight)];
    _inputBackgroundView.autoresizingMask = UIViewAutoresizingNone;
   _inputBackgroundView.contentMode = UIViewContentModeScaleToFill;
    _inputBackgroundView.userInteractionEnabled = YES;
    [self addSubview:_inputBackgroundView];
   [_inputBackgroundView release];

   [_inputBackgroundView setImage:[[UIImage imageNamed:@"Footer_BG.png"] stretchableImageWithLeftCapWidth:80 topCapHeight:25]];

    // Text field
    _textField = [[UITextView alloc] initWithFrame:CGRectMake(70.0f, 0, 185, 0)];
   _textField.backgroundColor = [UIColor clearColor];
    _textField.delegate = self;
   _textField.contentInset = UIEdgeInsetsMake(-4, -2, -4, 0);
   _textField.showsVerticalScrollIndicator = NO;
   _textField.showsHorizontalScrollIndicator = NO;
    _textField.font = [UIFont systemFontOfSize:15.0f];
    [_inputBackgroundView addSubview:_textField];
   [_textField release];

   [self adjustTextInputHeightForText:@""];

Fill UITextView delegate methods:

- (void) textViewDidBeginEditing:(UITextView*)textView {

   [self adjustTextInputHeightForText:_textField.text];
}

- (void) textViewDidEndEditing:(UITextView*)textView {

   [self adjustTextInputHeightForText:_textField.text];
}

- (BOOL) textView:(UITextView*)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString*)text {

   if ([text isEqualToString:@"\n"])
   {
      [self performSelector:@selector(inputComplete:) withObject:nil afterDelay:.1];
      return NO;
   }
   else if (text.length > 0)
   {
      [self adjustTextInputHeightForText:[NSString stringWithFormat:@"%@%@", _textField.text, text]];
   }
   return YES;
}

- (void) textViewDidChange:(UITextView*)textView {

   [self adjustTextInputHeightForText:_textField.text];
}

And the trick is...

- (void) adjustTextInputHeightForText:(NSString*)text {

   int h1 = [text sizeWithFont:_textField.font].height;
   int h2 = [text sizeWithFont:_textField.font constrainedToSize:CGSizeMake(_textField.frame.size.width - 16, 170.0f) lineBreakMode:UILineBreakModeWordWrap].height;

   [UIView animateWithDuration:.1f animations:^
   {
      if (h2 == h1)
      {
         _inputBackgroundView.frame = CGRectMake(0.0f, self.frame.size.height - _InputBarHeight, self.frame.size.width, _InputBarHeight);
      }
      else
      {
         CGSize size = CGSizeMake(_textField.frame.size.width, h2 + 24);
         _inputBackgroundView.frame = CGRectMake(0.0f, self.frame.size.height - size.height, self.frame.size.width, size.height);
      }
      CGRect r = _textField.frame;
      r.origin.y = 12;
      r.size.height = _inputBackgroundView.frame.size.height - 18;
      _textField.frame = r;

   } completion:^(BOOL finished)
   {
      //
   }];
}
m8labs
  • 3,671
  • 2
  • 30
  • 32
  • Hi, right now your code doesn't compile (e.g. _InputBarHeight, Footer_BG.png image, etc). If you could create a minimal project with your code and pass a link here that let everyone that needs it to download (github maybe?) it would be much appreciated by a LOT of people I believe, including me :) – ozba Nov 17 '12 at 22:57
  • 1
    You are welcome :) I created git repo with this sample https://github.com/alekperov/ChatInputSample – m8labs Nov 19 '12 at 21:14
  • @MaratAl Thank you very much! – GJain Apr 03 '16 at 03:58
2

Facebook has released an open-source package called Three20 that has a multi-line text field. You can use this pretty easily for an expanding text field.

As for the "Done" button, you can set your view controller as a UITextFieldDelegate. Then use this method:

 - (BOOL)textFieldShouldReturn:(UITextField *)textField {
     // Do whatever you want for your done button
      return YES;
 }

In the case of Three20, use this method of TTTextEditorDelegate:

 - (BOOL)textFieldShouldReturn:(TTTextEditor *)textField {
     // Do whatever you want for your done button
      return YES;
 }
Chris Long
  • 3,007
  • 4
  • 30
  • 37
  • Hi there... UITextView does not have a textViewShouldReturn method even when set as the UITextViewDelegate, so that does not work. I am also loathe to use the Three20 stuff as I have read reports of multiple applications being rejected from the app store because of the private APIs that Three20 uses. – Anonymouslemming Jan 10 '10 at 11:18
  • 2
    The private API references have long since been removed from Three20 (see the change list from 17 November 2009). As it was explained to me at the iPhone Tech Talk World Tour this past December, any code you end up not using gets stripped from release binaries anyway. See "Dead Code Stripping" in the Xcode Build System Guide (part of the iPhone OS Reference Library). – Joe D'Andrea Feb 01 '10 at 19:16
1

Well, I had a similar problem, and what I ended up using is actually create a disabled UITextField as the background and a UITextView above it to get the input... It sucks that iPhone API cannot have this by default. Also note that this does not auto-expand, but you can do this if you want by handling the textViewDidChange:

As for handling the return key, try implementing the following method from the UITextViewDelegate:

- (void)textViewDidChange:(UITextView *)inTextView {
    NSString *text = inTextView.text;

    if ([text length] > 0 && [text characterAtIndex:[text length] -1] == '\n') {
        inTextView.text = [text substringToIndex:[text length] -1]; // remove last return from text view
        [inTextView resignFirstResponder]; // hide keyboard
    }
}
Adam Woś
  • 2,493
  • 20
  • 21
  • Thanks for the advice on the UITextViewDelegate, but that won't work I'm afraid. As I want to allow multi-line input, I cannot scan for '\n' - this is why I felt the behaviour of 'Go' and 'Done' both generating \n is unhelpful. – Anonymouslemming Jan 10 '10 at 19:40
  • Well, the suggested solution (one I'm also using) would be to add a "Done" button to a `UINavigationBar` as the `UINavigationItem`'s `rightBarButtonItem`, and when it's clicked, just `resignFirstResponder` on your textview. Or, basically, just add any other element of the UI to resign the responder. The navigation bar seems widely used, though. – Adam Woś Jan 10 '10 at 19:42
0
  • (void)textEditorDidBeginEditing:(TTTextEditor *)textEditor { And

    • (void)textEditorDidEndEditing:(TTTextEditor *)textEditor { might be what you're looking for. Enjoy!