Mutliline text entry on iOS

Hi all,

I have multiline text editing on PC but on iOS the popup with the virtual keyboard only shows a single line, there were no callbacks to IVirtualKeyboardEntry::IsMultilineEntry.

A quick bit of reading suggests that’s a limitation of UITextField which appears to be the widget used in @implementation SlateTextField (forgive me if spouting crap, I’m not a big iOS developer).

Before I start digging deeper does anyone have any success/pointers in implementing multi line text editing support on iOS?

My ideal would be a full screen popup with title, top half of screen is the text and the bottom half the keyboard.

This was my solution (don’t use the UE4 keyboard, add some objectiveC). The sp_MultiLineTextEditComponent pointer is my own class that receives the text - a total kludge perhaps but might just ship with it.

In my cpp:

#if PLATFORM_IOS
#include "IOSAppDelegate.h"
#import <Foundation/Foundation.h>


@interface MultilineTextController()
@property (strong, nonatomic) UITextView *textField;
//@property (nonatomic, assign) UINavigationBar *navBar;
@property (nonatomic, assign) UIToolbar *navBar;
@property (nonatomic, assign) NSString *textInOut;
@end

@implementation MultilineTextController

+ (MultilineTextController*)GetDelegate
{
    static MultilineTextController * Singleton = [[MultilineTextController alloc] init];
    return Singleton;
}

-(void)setupTextEditor:(NSString*)text
{
    _textInOut = text;
    [[IOSAppDelegate GetDelegate].IOSController presentViewController : self animated : NO completion : nil];
}

-(void)viewWillAppear:(BOOL)animated
{
    _textField.text = _textInOut;
    [_textField becomeFirstResponder];
    
    [super viewWillAppear:animated];
}

-(void)viewDidLoad
{
    //Setup the nav bar
    _navBar = [[UIToolbar alloc] initWithFrame:CGRectMake(5, 5, self.view.bounds.size.width-10, 30)];
    UIBarButtonItem* spacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
    UIBarButtonItem* doneBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(doneTextEditing)];
    UIBarButtonItem* cancelBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelTextEditing)];
    _navBar.items = [NSArray arrayWithObjects:cancelBtn, spacer, doneBtn, nil];
    
    //Setup the text field
    _textField=[[UITextView alloc] initWithFrame:CGRectMake(5, _navBar.frame.size.height, self.view.bounds.size.width-10, self.view.bounds.size.height-10)];
    _textField.delegate = self;
    _textField.scrollEnabled=YES;
    _textField.userInteractionEnabled = YES;
    _textField.editable=YES;
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardDidShowNotification object:nil];

    //Add the views and pass it on
    [self.view addSubview:_textField];
    [self.view addSubview:_navBar];
    [super viewDidLoad];
}


- (void)keyboardWasShown:(NSNotification*)aNotification
{
    NSValue *keyboardRectAsObject =
    [[aNotification userInfo]
     objectForKey:UIKeyboardFrameEndUserInfoKey];
    
    
    CGRect keyboardRect = CGRectZero;
    [keyboardRectAsObject getValue:&keyboardRect];
    
    
    _textField.contentInset =
    UIEdgeInsetsMake(0.0f,
                     0.0f,
                     keyboardRect.size.height + _navBar.frame.size.height,
                     0.0f);
}

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    
    CGFloat offset = _navBar.frame.size.height+[UIApplication sharedApplication].statusBarFrame.size.height;
    _textField.frame = CGRectMake(5, offset, self.view.bounds.size.width-10, self.view.bounds.size.height-10);
}

-(void)doneTextEditing
{
    _textInOut = _textField.text;
    if (sp_MultiLineTextEditComponent)
    {
        sp_MultiLineTextEditComponent->SetText(_textField.text);
        sp_MultiLineTextEditComponent = nullptr;
    }
    [_textField resignFirstResponder];
    [self dismissViewControllerAnimated:NO completion:nil];
}

-(void)cancelTextEditing
{
    [_textField resignFirstResponder];
    [self dismissViewControllerAnimated:NO completion:nil];
}

-(void)viewDidUnload
{
    self.textField = nil;
    self.navBar = nil;
    self.textInOut = nil;
}

+(void)openMultilineTextEditor:(NSString*)text
{
    // perform this action on the iOS main thread
    [[MultilineTextController GetDelegate] performSelectorOnMainThread:@selector(setupTextEditor:) withObject:text waitUntilDone : NO];
}
@end
#endif

In my .h file:

#if PLATFORM_IOS
#import <UIKit/UIKit.h>

// UIImagePickerControllerDelegate to respond to user interactions
// UINavigationControllerDelegate because we want to present the photo library modally
@interface MultilineTextController : UIViewController <UITextViewDelegate>

// function to run in iOS main thread
+ (void) openMultilineTextEditor:(NSString*)text;

@end
#endif