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

[iOS] Resubmit #24714 and fixes kvo crash #24840

Closed

Conversation

zhongwuzw
Copy link
Contributor

@zhongwuzw zhongwuzw commented May 14, 2019

Summary

Resubmit #24714.
From the #24714 (comment), seems it would crash sometimes. @JoshuaGross Hi :) I added a method to let UITextField call the cleanup explicitly. Please check again, thanks!

Changelog

[iOS] [Fixed] - Fixes selection of single line text input

Test Plan

Create a single line textInput, then do some selection, onSelectionChange can be called.

@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 14, 2019
@react-native-bot react-native-bot added Platform: iOS iOS applications. Bug labels May 14, 2019
@cpojer cpojer requested a review from JoshuaGross May 14, 2019 10:01
@JoshuaGross
Copy link
Contributor

Hi @zhongwuzw, thanks for working on this. Do you think it's possible to do this without KVO, or without KVO on unowned objects? KVO has a lot of pitfalls and this use-case is tricky. Could we, for example, have the RCTUITextField observe itself and call delegate methods when relevant?

@zhongwuzw
Copy link
Contributor Author

@JoshuaGross Agree! 👍 Here we go! no KVO and override setSelectedTextRange instead.

@JoshuaGross
Copy link
Contributor

Can you squash your commits?

@@ -176,6 +176,16 @@ - (CGRect)editingRectForBounds:(CGRect)bounds

#pragma mark - Overrides

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, why do we need these pragmas? This seems like a sign that this might not be the right solution.

Copy link
Contributor Author

@zhongwuzw zhongwuzw May 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we need it to suppress warning, warning because we mark setSelectedTextRange as unavailable, please see :

// This protocol disallows direct access to `selectedTextRange` property because
// unwise usage of it can break the `delegate` behavior. So, we always have to
// explicitly specify should `delegate` be notified about the change or not.
// If the change was initiated programmatically, we must NOT notify the delegate.
// If the change was a result of user actions (like typing or touches), we MUST notify the delegate.
- (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange NS_UNAVAILABLE;
- (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange notifyDelegate:(BOOL)notifyDelegate;

We disallow access setSelectedTextRange directly, but Apple framework would call it, and this is how KVO works :).

- (void)setSelectedTextRange:(UITextRange *)selectedTextRange
{
[super setSelectedTextRange:selectedTextRange];
[_textInputDelegateAdapter selectedTextRangeWasSet];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we try to call delegates in the same order? In the method below this one, we call the delegate method and then we call super; here, it's reversed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Emm, may not have much difference IMO, but I can consolidate them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JoshuaGross I reverted to this change, we need to call super first, otherwise, onSelectionChange would be called with last args, not latest args.

@JoshuaGross
Copy link
Contributor

Thanks again for doing this! I have some specific concerns but I'm excited to get this in once we know we're doing everything the right way :)

@zhongwuzw zhongwuzw force-pushed the fix_textinput_selectedRange branch from 40e9923 to e7d6441 Compare May 16, 2019 01:29
Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JoshuaGross has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@JoshuaGross
Copy link
Contributor

Hi @zhongwuzw, I'm testing this PR with the following example and seeing that selection changes are happening, but not really getting useful information about the selection changes that I can tell. Is this what you would expect? Is there a better test-case for this?

/**
 * Copyright 2004-present Facebook. All Rights Reserved.
 *
 * @flow strict-local
 * @format
 */

'use strict';
import React, { Component } from 'react';
import {StyleSheet, Text, View} from 'react-native';
import { TextInput } from 'react-native';

class Playground extends Component {
  constructor(props) {
    super(props);
    this.state = { text: 'Useless Placeholder', selectionChange: null };
  }

  render() {
    return (
      <View style={styles.container}>
        <TextInput
          style={{height: 40, borderColor: 'gray', borderWidth: 1}}
          onChangeText={(text) => this.setState({...this.state, text}) && console.log('onChangeText: ', text)}
          onSelectionChange={(...args) => this.setState({...this.state, selectionChange: args}) && console.log('onSelectionChange: ', args)}
          value={this.state.text}
        />
        <Text>Text: {this.state.text}</Text>
        <Text>Selection: {JSON.stringify((this.state.selectionChange))}</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: 'white',
    padding: 10,
    paddingTop: 30,
  },
});

export default Playground;

Screen Shot 2019-05-20 at 11 33 53 AM

@zhongwuzw
Copy link
Contributor Author

@JoshuaGross I amended the code based yours, please see:

  constructor(props) {
    super(props);
    this.state = { text: 'Useless Placeholder', selectionChange: null };
  }

  onSelectionChange({nativeEvent: {selection}}) {
    this.setState({selection});
  }

  render() {
    return (
      <View style={styles.container}>
        <TextInput
          style={{height: 40, borderColor: 'gray', borderWidth: 1}}
          onChangeText={(text) => this.setState({...this.state, text}) && console.log('onChangeText: ', text)}
          onSelectionChange={this.onSelectionChange.bind(this)}
          value={this.state.text}
        />
        <Text>Text: {this.state.text}</Text>
        <Text>selection = {JSON.stringify(this.state.selection)}</Text>
      </View>
    );
  }
}

image

@zhongwuzw zhongwuzw force-pushed the fix_textinput_selectedRange branch from 2e5dc90 to d7f5c61 Compare May 21, 2019 01:40
Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JoshuaGross has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@react-native-bot
Copy link
Collaborator

This pull request was successfully merged by @zhongwuzw in c38f167.

When will my fix make it into a release? | Upcoming Releases

@react-native-bot react-native-bot added the Merged This PR has been merged. label May 21, 2019
M-i-k-e-l pushed a commit to M-i-k-e-l/react-native that referenced this pull request Mar 10, 2020
Summary:
Resubmit facebook#24714.
From the facebook#24714 (comment), seems it would crash sometimes. JoshuaGross Hi :) I added a method to let `UITextField` call the cleanup explicitly. Please check again, thanks!

## Changelog

[iOS] [Fixed] - Fixes selection of single line text input
Pull Request resolved: facebook#24840

Differential Revision: D15415793

Pulled By: JoshuaGross

fbshipit-source-id: 2bb32aa8a1fd8fad116177aac760509649c4a423
@MrXyfir
Copy link

MrXyfir commented Oct 23, 2020

Would be nice to update the docs that multiline={true} is no longer required for onSelectionChange.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. Platform: iOS iOS applications.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants