Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TextInput - Multiline] Reimplemented RCTTextView and added auto height scaling. #1229

Closed
wants to merge 7 commits into from

Conversation

lukasredev
Copy link

I started with the goal to add auto height scaling functionality to the RCTTextViewclass. As discussed in this thread.
I soon noticed, that I would have to rewrite most of RCTTextView and create at least an object on the shadow thread for it: RCTShadowTextView.

I tried to implement the RCTShadowTextView the same way as RCTShadowText, so there is as much consistency as possible. I did not change the interface of the current RCTTextView implementation, so there shouldn't be any problems with updating an existing application.

Implemented auto height scaling for TextView

The RCTShadowTextView class implements the Measure function of css-layout. If the TextInput componenet does not have a specified height, it will automatically scale to the height that matches the text. It calculates the height using NSLayoutContaniner because UITextView is not thread safe.
Note: It's important to disable scrolling when using the auto scaling.

Data Flow

The displayed text is sent from JS to the object on the Shadow Thread. The shadow object is responsible for generating a NSAttributedString (Same approach as in RCTShadowText).

Updates from javascript go the following way:

JS Object --(NSString)--> RCTShadowTextView --(NSAttributedString)--> RCTShadowText

Updates from UITextViewgo the following way:

RCTShadowText --(Event)--> JS Object --(NSString)--> RCTShadowTextView

Please note that the update chain stops after RCTShadowTextView. This way the UITextView is only updated when we change something from javascript, but not if the user types somehting into the UITextView. In this case we only update the Shadow object. This approach is necessary because we can not constantly reset the UITextView.attributedText property, because this will also remove any autocorrections.

Created the RCTAttributedStringHandler class

The RCTAttributedStringHandler stores text styling attributes. It can be used to generate a styled NSAttributedString from a NSString.
The process of generating the NSAttributedString was pretty much copied from RCTShadowText. The reason why I moved it into a separate object is, that RCTShadowTextView needs to store text attributes for two different Strings. The text string and the placeholder string. (Although they share most of the attributes, the approach to move this functionality into a separate object is much cleaner).

Rewrote the uiBlockToAmendWithShadowRegistry

Rewrote the (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry function of RCTTextManager and RCTTextViewManager, so that RCTShadowText and RCTShadowTextView are both able to use the
textLifecycle variable of RCTShadowView.

Small Additions / Notes
  • Added a scrollEnabled property to RCTTextView
  • Removed all iOS default padding from the UITextView. I think this is the best solution, because this way we can fully control the appearance by using the css properties.
  • Updated the UIExplorer example with one additional multiline TextInput that uses the new auto height scaling feature.
  • Currently it's not possible to add a subview to my new implementation of RCTTextView, because of a problem with the css-layout project. I already adressed this problem in the following pull request. If you want to try it out on your own just replace the code on line 318 with the following snippet: if (node.children.length === 0) { return; }. This may also be the first step to be able to embed text nodes.

Any additions and questions are welcome. 👍

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label May 10, 2015
@brentvatne
Copy link
Collaborator

@lukasreichart - really interesting stuff! Curious what @nicklockwood has to say about it

@ide
Copy link
Contributor

ide commented May 12, 2015

Wow this looks great.

Note: It's important to disable scrolling when using the auto scaling.

Why is this?

@sahrens
Copy link
Contributor

sahrens commented May 13, 2015

Also cc @a2

@vjeux vjeux assigned a2 May 14, 2015
@brentvatne brentvatne changed the title Reimplemented RCTTextView and added auto height scaling. [TextInput - Multiline] Reimplemented RCTTextView and added auto height scaling. May 30, 2015
@sjmueller
Copy link
Contributor

Anything preventing this one from being merged? It's much needed functionality for TextInput, but this PR has been pretty quiet the last month.

…lso working correctly with the calculation of the height.
@lukasredev
Copy link
Author

@sjmueller I am waiting for the review of @a2 ....

@qingfeng
Copy link
Contributor

cool, guys 🍺

@lukasredev
Copy link
Author

@brentvatne @ide @a2 @nicklockwood any progress here? 👍

@a2 a2 assigned nicklockwood and unassigned a2 Jun 18, 2015
@JonasJonny
Copy link

👍

@brentvatne
Copy link
Collaborator

Unfortunately not @lukasreichart - I think we just need to hold up until someone has time to properly review :(

@nicklockwood
Copy link
Contributor

Sorry for the delay. It's a complex PR, so it will take a couple of days of somebody's time to review and test, and we're a small team with a long task list :-) We'll get to it eventually though.

@Sing-Li
Copy link

Sing-Li commented Aug 1, 2015

👍 Hope this gets merged soon. While not road-block, it is a monumental task to implement similar behaviors otherwise 😦

@yodaiken
Copy link

yodaiken commented Aug 4, 2015

Hi just want to echo that this is a really key feature!

@lukasredev
Copy link
Author

@nicklockwood I just noticed, that have you have rewritten some of the RCTText and RCTTextView code since I've created this PR. I could rewrite my PR so it would be easier to merge for you. Would this help? I really want to close this, because this feature makes working with text so much easier.

@adbl
Copy link
Contributor

adbl commented Aug 27, 2015

+1

@sjmueller
Copy link
Contributor

@lukasreichart it would be awesome if you could rewrite the PR. Many of us are using your changes manually, and it would be great to have a version that works with the latest build of RN.

@deanmcpherson
Copy link
Contributor

Any move on this?

@nicklockwood
Copy link
Contributor

Hey, really sorry for the radio silence on this. It is an important feature, but as you say we now have two competing solutions, both of which are very complex to review, and it affects a critical component used in most of our apps.

I haven't forgotten about this, and I'll try to get one or other solution merged in the near future.

@christopherdro
Copy link
Contributor

👍 Thanks @nicklockwood!

@kinhunt
Copy link

kinhunt commented Nov 26, 2015

greate

On Thu, Nov 26, 2015 at 7:59 PM, Christopher Dro notifications@github.com
wrote:

[image: 👍] Thanks @nicklockwood https://github.com/nicklockwood!


Reply to this email directly or view it on GitHub
#1229 (comment)
.

@cgilboy
Copy link

cgilboy commented Dec 10, 2015

Are there any updates to this or PR #3097? We would really like to see this functionality merged in :)

@jeremiecarlson
Copy link

+1

@brentvatne
Copy link
Collaborator

@cgilboy - as mentioned above, Nick is working on it! There's truly no better person to have on the job, so it's in good hands :)

@martincik
Copy link

+1 for merge, please.

@sebglazebrook
Copy link

+1 for merge

@brentvatne
Copy link
Collaborator

@manask88
Copy link
Contributor

hows this going? any plan to merge/ work on this PR soon?

ghost pushed a commit that referenced this pull request Jan 25, 2016
Summary:
public
This diff adds support for auto-resizing multiline text fields. This has been a long-requested feature, with several native solutions having been proposed (see #1229 and D2846915).

Rather than making this a feature of the native component, this diff simply exposes some extra information in the `onChange` event that makes it easy to implement this in pure JS code. I think this is preferable, since it's simpler, works cross-platform, and avoids any controversy about what the API should look like, or how the props should be named. It also makes it easier to implement custom min/max-height logic.

Reviewed By: sahrens

Differential Revision: D2849889

fb-gh-sync-id: d9ddf4ba4037d388dac0558aa467d958300aa691
@nicklockwood
Copy link
Contributor

Sorry for the long silence on this. At the time this PR was created, calculating the text layout on the shadow queue made a lot of sense, but now that RCTTextView is making heavy use of the scrollview.contentSize anyway, there's not really any performance gain to doing this work in the background, and it massively increases the complexity.

I did try rebasing this against the latest codebase, but it seems to be broken in a way that I can't fix, and in light of the much simpler solution I've implemented in 481f560, I'm not sure it's worth trying to revive this.

Thanks for all your efforts, and sorry we didn't manage to make this work.

doostin pushed a commit to doostin/react-native that referenced this pull request Feb 1, 2016
Summary:
public
This diff adds support for auto-resizing multiline text fields. This has been a long-requested feature, with several native solutions having been proposed (see facebook#1229 and D2846915).

Rather than making this a feature of the native component, this diff simply exposes some extra information in the `onChange` event that makes it easy to implement this in pure JS code. I think this is preferable, since it's simpler, works cross-platform, and avoids any controversy about what the API should look like, or how the props should be named. It also makes it easier to implement custom min/max-height logic.

Reviewed By: sahrens

Differential Revision: D2849889

fb-gh-sync-id: d9ddf4ba4037d388dac0558aa467d958300aa691
jaysoo pushed a commit to jaysoo/react-native that referenced this pull request Feb 2, 2016
Summary:
public
This diff adds support for auto-resizing multiline text fields. This has been a long-requested feature, with several native solutions having been proposed (see facebook#1229 and D2846915).

Rather than making this a feature of the native component, this diff simply exposes some extra information in the `onChange` event that makes it easy to implement this in pure JS code. I think this is preferable, since it's simpler, works cross-platform, and avoids any controversy about what the API should look like, or how the props should be named. It also makes it easier to implement custom min/max-height logic.

Reviewed By: sahrens

Differential Revision: D2849889

fb-gh-sync-id: d9ddf4ba4037d388dac0558aa467d958300aa691
@deanmcpherson
Copy link
Contributor

Hey @nicklockwood, quick question - the onChange event that we depend upon here only fires when a user enters text via a keyboard. This means that if there is more than one line of text loaded in (e.g. resuming a form), the height will be restricted to whatever the default is. Is there any way to trigger (or measure) the contentSize so that we can set an accurate initial height?

@brentvatne
Copy link
Collaborator

Great point @deanmcpherson, I'm also curious about this

@deanmcpherson
Copy link
Contributor

So I've have had a go at firing the same onChange event in the updateContentSize method of RCTTextView.m, which works correctly in adjusting the textinput height to the value automagically. It doesn't appear to be firing too often, but obviously isn't the cleanest way of handling this.

I did have a go at creating a separate event name to listen on for inputs, (e.g. onContentSize) but couldn't quite get it to come through correctly. Though I imagine throwing in custom events isn't really a great way forward either.

Thoughts @nicklockwood @brentvatne?

@nicklockwood
Copy link
Contributor

What we ideally want here is to:

  1. Only call onChange once per set of changes (e.g. if text and contentSize both change at once - which is usually the case - we should only fire one onChange event)
  2. Only call onChange if something has actually changed (i.e. don't fire it unconditionally in updateContentSize if updateContentSize sometimes gets called when the contentSize hasn't changed).

The former can be ensured by only firing onChange in one place. The latter can be ensured by keeping track of the previous contentSize, text, etc. and comparing them before firing the event.

If you have a solution that seems to work, put up a PR and we can review it and suggest changes if needed.

@brentvatne
Copy link
Collaborator

Thanks @nicklockwood! @deanmcpherson - do you want to try to PR this suggested approach? @mkonicek we need something similar for Android too

@deanmcpherson
Copy link
Contributor

I've noticed a few instances where my implementation isn't working, in particular using <Text> children instead of value for dynamic styling of content seems to sometimes set an incorrect height sometimes. I'm guessing that there is some kind of race condition happening. I imagine that will be resolved by following 1) and 2) above. I'm happy to try a PR for this.

@nicklockwood do you think it makes sense for onChange to fire when no text value has changed? For most uses of TextInput where the contentSize isn't important, it might be unexpected behaviour for the user.

@nicklockwood
Copy link
Contributor

@deanmcpherson

do you think it makes sense for onChange to fire when no text value has changed?

I agree it's not ideal, but it's the least bad option I can think of, and according to @dmmiller that's how it works on Android currently (unless I misunderstood?)

@nicklockwood
Copy link
Contributor

What we should probably do is add a separate onChangeContentSize event which can be observerved independently of onTextChange.

Let's get the APIs consistent between platforms before we start changing them though.

@yuvraj24
Copy link

yuvraj24 commented Apr 7, 2020

For auto-resizing of multiline TextInput use ,

flexGrow:1

Found this working on both iOS & Android devices.

ayushjainrksh pushed a commit to MLH-Fellowship/react-native that referenced this pull request Jul 2, 2020
ryanlntn pushed a commit to ryanlntn/react-native that referenced this pull request Aug 9, 2022
…rn/parse-url-6.0.2

Bump parse-url from 6.0.0 to 6.0.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.