Nativescript: Prevent newline in editable TextView?

Created on 20 Jan 2016  Â·  11Comments  Â·  Source: NativeScript/NativeScript

returnPress doesn't seem to work on a TextView (at least on ios), and a newline is created. Is there a way to prevent the newline and capture the returnPress?

question

Most helpful comment

Here is a little example that shows how to aggregate the default UITextView delegate that we've created into your own delegate and modify the behavior that you need.

main-page.xml

<Page loaded="onPageLoaded">
  <StackLayout id="stack" backgroundColor="LightGray">
    <TextView id="textView" text="text-view" backgroundColor="Blue" height="100" returnKeyType="next"/>
    <TextField id="textField" text="text-field" hint="hint" backgroundColor="Red"/>
  </StackLayout>
</Page>

main-page.ts (TypeScript)

import observableModule = require("data/observable");
import pagesModule = require("ui/page");
import textViewModule = require("ui/text-view");

var textView: textViewModule.TextView;
var _myDelegate: MyUITextViewDelegateImpl;

export function onPageLoaded(args: observableModule.EventData) {
    textView = (<pagesModule.Page>args.object).getViewById<textViewModule.TextView>("textView");
    if (textView.ios) {
        _myDelegate = MyUITextViewDelegateImpl.initWithOwner(new WeakRef(textView));
        textView.ios.delegate = _myDelegate;
    }
}

class MyUITextViewDelegateImpl extends NSObject implements UITextViewDelegate {
    public static ObjCProtocols = [UITextViewDelegate];

    private _owner: WeakRef<textViewModule.TextView>;
    private _originalDelegate: UITextViewDelegate;

    public static initWithOwner(owner: WeakRef<textViewModule.TextView>): MyUITextViewDelegateImpl {
        let impl = <MyUITextViewDelegateImpl>MyUITextViewDelegateImpl.new();
        impl._owner = owner;
        impl._originalDelegate = <UITextViewDelegate>(<UITextView>owner.get().ios).delegate;
        return impl;
    }

    public textViewShouldBeginEditing(textView: UITextView): boolean {
        return this._originalDelegate.textViewShouldBeginEditing(textView);
    }

    public textViewDidBeginEditing(textView: UITextView) {
        this._originalDelegate.textViewDidBeginEditing(textView);
    }

    public textViewDidEndEditing(textView: UITextView) {
        this._originalDelegate.textViewDidEndEditing(textView);
    }

    public textViewDidChange(textView: UITextView) {
        this._originalDelegate.textViewDidChange(textView);
    }

    public textViewShouldChangeTextInRangeReplacementText(textView: UITextView, range: NSRange, text: string): boolean {
        console.log(`MyUITextViewDelegateImpl.textViewShouldChangeTextInRangeReplacementText(${text})`);
        if (text === "\n") {
            console.log(`textView.resignFirstResponder();`);
            textView.resignFirstResponder();
            return false;
        }
        return true;
    }
}

main-page.ts (JavaScript)

var textView;
var _myDelegate;
function onPageLoaded(args) {
    textView = args.object.getViewById("textView");
    if (textView.ios) {
        _myDelegate = MyUITextViewDelegateImpl.initWithOwner(new WeakRef(textView));
        textView.ios.delegate = _myDelegate;
    }
}
exports.onPageLoaded = onPageLoaded;
var MyUITextViewDelegateImpl = (function (_super) {
    __extends(MyUITextViewDelegateImpl, _super);
    function MyUITextViewDelegateImpl() {
        _super.apply(this, arguments);
    }
    MyUITextViewDelegateImpl.initWithOwner = function (owner) {
        var impl = MyUITextViewDelegateImpl.new();
        impl._owner = owner;
        impl._originalDelegate = owner.get().ios.delegate;
        return impl;
    };
    MyUITextViewDelegateImpl.prototype.textViewShouldBeginEditing = function (textView) {
        return this._originalDelegate.textViewShouldBeginEditing(textView);
    };
    MyUITextViewDelegateImpl.prototype.textViewDidBeginEditing = function (textView) {
        this._originalDelegate.textViewDidBeginEditing(textView);
    };
    MyUITextViewDelegateImpl.prototype.textViewDidEndEditing = function (textView) {
        this._originalDelegate.textViewDidEndEditing(textView);
    };
    MyUITextViewDelegateImpl.prototype.textViewDidChange = function (textView) {
        this._originalDelegate.textViewDidChange(textView);
    };
    MyUITextViewDelegateImpl.prototype.textViewShouldChangeTextInRangeReplacementText = function (textView, range, text) {
        console.log("MyUITextViewDelegateImpl.textViewShouldChangeTextInRangeReplacementText(" + text + ")");
        if (text === "\n") {
            console.log("textView.resignFirstResponder();");
            textView.resignFirstResponder();
            return false;
        }
        return true;
    };
    MyUITextViewDelegateImpl.ObjCProtocols = [UITextViewDelegate];
    return MyUITextViewDelegateImpl;
})(NSObject);

All 11 comments

Do you need single line text edit? In this case you may need to use TextField instead.

I want the text to wrap so that the user can write a long string and still
see all of it, but I want to capture the return and bring focus to the next
TextView, or hide the soft keyboard.
On Wed, Jan 20, 2016 at 1:02 AM Vladimir Enchev [email protected]
wrote:

Do you need single line text edit? In this case you may need to use
TextField instead.

—
Reply to this email directly or view it on GitHub
https://github.com/NativeScript/NativeScript/issues/1404#issuecomment-173134568
.

I will see how this can be done. Stay tuned.

We will need to hack the UITextView as described here: http://stackoverflow.com/questions/4848086/how-do-i-dismiss-a-uitextview-using-the-done-keyboard-button. I am preparing a custom solution for you.

Here is a little example that shows how to aggregate the default UITextView delegate that we've created into your own delegate and modify the behavior that you need.

main-page.xml

<Page loaded="onPageLoaded">
  <StackLayout id="stack" backgroundColor="LightGray">
    <TextView id="textView" text="text-view" backgroundColor="Blue" height="100" returnKeyType="next"/>
    <TextField id="textField" text="text-field" hint="hint" backgroundColor="Red"/>
  </StackLayout>
</Page>

main-page.ts (TypeScript)

import observableModule = require("data/observable");
import pagesModule = require("ui/page");
import textViewModule = require("ui/text-view");

var textView: textViewModule.TextView;
var _myDelegate: MyUITextViewDelegateImpl;

export function onPageLoaded(args: observableModule.EventData) {
    textView = (<pagesModule.Page>args.object).getViewById<textViewModule.TextView>("textView");
    if (textView.ios) {
        _myDelegate = MyUITextViewDelegateImpl.initWithOwner(new WeakRef(textView));
        textView.ios.delegate = _myDelegate;
    }
}

class MyUITextViewDelegateImpl extends NSObject implements UITextViewDelegate {
    public static ObjCProtocols = [UITextViewDelegate];

    private _owner: WeakRef<textViewModule.TextView>;
    private _originalDelegate: UITextViewDelegate;

    public static initWithOwner(owner: WeakRef<textViewModule.TextView>): MyUITextViewDelegateImpl {
        let impl = <MyUITextViewDelegateImpl>MyUITextViewDelegateImpl.new();
        impl._owner = owner;
        impl._originalDelegate = <UITextViewDelegate>(<UITextView>owner.get().ios).delegate;
        return impl;
    }

    public textViewShouldBeginEditing(textView: UITextView): boolean {
        return this._originalDelegate.textViewShouldBeginEditing(textView);
    }

    public textViewDidBeginEditing(textView: UITextView) {
        this._originalDelegate.textViewDidBeginEditing(textView);
    }

    public textViewDidEndEditing(textView: UITextView) {
        this._originalDelegate.textViewDidEndEditing(textView);
    }

    public textViewDidChange(textView: UITextView) {
        this._originalDelegate.textViewDidChange(textView);
    }

    public textViewShouldChangeTextInRangeReplacementText(textView: UITextView, range: NSRange, text: string): boolean {
        console.log(`MyUITextViewDelegateImpl.textViewShouldChangeTextInRangeReplacementText(${text})`);
        if (text === "\n") {
            console.log(`textView.resignFirstResponder();`);
            textView.resignFirstResponder();
            return false;
        }
        return true;
    }
}

main-page.ts (JavaScript)

var textView;
var _myDelegate;
function onPageLoaded(args) {
    textView = args.object.getViewById("textView");
    if (textView.ios) {
        _myDelegate = MyUITextViewDelegateImpl.initWithOwner(new WeakRef(textView));
        textView.ios.delegate = _myDelegate;
    }
}
exports.onPageLoaded = onPageLoaded;
var MyUITextViewDelegateImpl = (function (_super) {
    __extends(MyUITextViewDelegateImpl, _super);
    function MyUITextViewDelegateImpl() {
        _super.apply(this, arguments);
    }
    MyUITextViewDelegateImpl.initWithOwner = function (owner) {
        var impl = MyUITextViewDelegateImpl.new();
        impl._owner = owner;
        impl._originalDelegate = owner.get().ios.delegate;
        return impl;
    };
    MyUITextViewDelegateImpl.prototype.textViewShouldBeginEditing = function (textView) {
        return this._originalDelegate.textViewShouldBeginEditing(textView);
    };
    MyUITextViewDelegateImpl.prototype.textViewDidBeginEditing = function (textView) {
        this._originalDelegate.textViewDidBeginEditing(textView);
    };
    MyUITextViewDelegateImpl.prototype.textViewDidEndEditing = function (textView) {
        this._originalDelegate.textViewDidEndEditing(textView);
    };
    MyUITextViewDelegateImpl.prototype.textViewDidChange = function (textView) {
        this._originalDelegate.textViewDidChange(textView);
    };
    MyUITextViewDelegateImpl.prototype.textViewShouldChangeTextInRangeReplacementText = function (textView, range, text) {
        console.log("MyUITextViewDelegateImpl.textViewShouldChangeTextInRangeReplacementText(" + text + ")");
        if (text === "\n") {
            console.log("textView.resignFirstResponder();");
            textView.resignFirstResponder();
            return false;
        }
        return true;
    };
    MyUITextViewDelegateImpl.ObjCProtocols = [UITextViewDelegate];
    return MyUITextViewDelegateImpl;
})(NSObject);

@shantamg Let me know if this does not help.

Awesome, I'll try it out as soon as I can get a moment.

Thanks for your efforts... It does work on the one TextView. It removes focus when the enter key is hit. It doesn't bring focus to the next TextView, though that can be worked out.

The main issue is that it must be explicitly performed on each TextView. In my case there is a TextView inside of a Repeater so when new data is pushed, and a NewText view is created, it doesn't have this functionality.

For now, I'll go back to using TextField and forgetting about letting it wrap. It's a trade off, but feels much cleaner.

@hamorphis It was really helpful for me :) Thanks.

@hamorphis how and where to define "MyUITextViewDelegateImpl".
`import observableModule = require("data/observable");
import pagesModule = require("ui/page");
import textViewModule = require("ui/text-view");

var textView: textViewModule.TextView;
var _myDelegate: MyUITextViewDelegateImpl;

export function onPageLoaded(args: observableModule.EventData) {
textView = (args.object).getViewById("textView");
if (textView.ios) {
_myDelegate = MyUITextViewDelegateImpl.initWithOwner(new WeakRef(textView));
textView.ios.delegate = _myDelegate;
}
}

class MyUITextViewDelegateImpl extends NSObject implements UITextViewDelegate {
public static ObjCProtocols = [UITextViewDelegate];

private _owner: WeakRef<textViewModule.TextView>;
private _originalDelegate: UITextViewDelegate;

public static initWithOwner(owner: WeakRef<textViewModule.TextView>): MyUITextViewDelegateImpl {
    let impl = <MyUITextViewDelegateImpl>MyUITextViewDelegateImpl.new();
    impl._owner = owner;
    impl._originalDelegate = <UITextViewDelegate>(<UITextView>owner.get().ios).delegate;
    return impl;
}

public textViewShouldBeginEditing(textView: UITextView): boolean {
    return this._originalDelegate.textViewShouldBeginEditing(textView);
}

public textViewDidBeginEditing(textView: UITextView) {
    this._originalDelegate.textViewDidBeginEditing(textView);
}

public textViewDidEndEditing(textView: UITextView) {
    this._originalDelegate.textViewDidEndEditing(textView);
}

public textViewDidChange(textView: UITextView) {
    this._originalDelegate.textViewDidChange(textView);
}

public textViewShouldChangeTextInRangeReplacementText(textView: UITextView, range: NSRange, text: string): boolean {
    console.log(`MyUITextViewDelegateImpl.textViewShouldChangeTextInRangeReplacementText(${text})`);
    if (text === "\n") {
        console.log(`textView.resignFirstResponder();`);
        textView.resignFirstResponder();
        return false;
    }
    return true;
}

}`

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Pourya8366 picture Pourya8366  Â·  3Comments

minjunlan picture minjunlan  Â·  3Comments

hshristov picture hshristov  Â·  3Comments

valentinstoychev picture valentinstoychev  Â·  3Comments

nirsalon picture nirsalon  Â·  3Comments