2017-06-27 23:05:05 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#import "RCTTextInput.h"
|
|
|
|
|
iOS: Support allowFontScaling on TextInput
Summary:
Currently, only `Text` supports the `allowFontScaling` prop. This commit adds support for it on `TextInput`.
As part of this change, the TextInput setters for font attributes (e.g. size, weight) had to be refactored. The problem with them is that they use RCTFont's helpers which create a new font based on an existing font. These helpers lose information. In particular, they lose the scaleMultiplier.
For example, suppose the font size is 12 and the device's font multiplier is set to 1.5. So we'd create a font with size 12 and scaleMultiplier 1.5 which is an effective size of 18 (which is the only thing stored in the font). Next, suppose the device's font multiplier changes to 1. So we'd use an RCTFont helper to create a new font based on the existing font but with a scaleMultiplier of 1. However, the font didn't store the font size (12) and scaleMultiplier (1.5) separately. It just knows the (effective) font size of 18. So RCTFont thinks the new font has a font size of 18 and a scaleMultiplier of 1 so its effective font size is 18. This is incorrect and it should have been 12.
To fix this, the font attributes are now all stored individually. Anytime one of them changes, updateFont is called which recreates the font from scratch. This happens to fix some bugs around fontStyle and fontWeight which were reported several times before: #13730, #12738, #2140, #8533.
Created a test app where I verified that `allowFontScaling` works properly for `TextInputs` for all values (`undefined`, `true`, `false`) for a variety of `TextInputs`:
- Singleline TextInput
- Singleline TextInput's placeholder
- Multiline TextInput
- Multiline TextInput's placeholder
- Multiline TextInput using children instead of `value`
Also, verified switching `fontSize`, `fontWeight`, `fontStyle` and `fontFamily` through a bunch of combinations works properly.
Lastly, my team has been using this change in our app.
Adam Comella
Microsoft Corp.
Closes https://github.com/facebook/react-native/pull/14030
Reviewed By: TheSavior
Differential Revision: D5899959
Pulled By: shergin
fbshipit-source-id: c8c8c4d4d670cd2a142286e79bfffef3b58cecd3
2017-10-02 04:40:57 +00:00
|
|
|
#import <React/RCTAccessibilityManager.h>
|
2017-06-27 23:05:05 +00:00
|
|
|
#import <React/RCTBridge.h>
|
|
|
|
#import <React/RCTConvert.h>
|
|
|
|
#import <React/RCTEventDispatcher.h>
|
2017-06-27 23:05:08 +00:00
|
|
|
#import <React/RCTUIManager.h>
|
iOS: Support allowFontScaling on TextInput
Summary:
Currently, only `Text` supports the `allowFontScaling` prop. This commit adds support for it on `TextInput`.
As part of this change, the TextInput setters for font attributes (e.g. size, weight) had to be refactored. The problem with them is that they use RCTFont's helpers which create a new font based on an existing font. These helpers lose information. In particular, they lose the scaleMultiplier.
For example, suppose the font size is 12 and the device's font multiplier is set to 1.5. So we'd create a font with size 12 and scaleMultiplier 1.5 which is an effective size of 18 (which is the only thing stored in the font). Next, suppose the device's font multiplier changes to 1. So we'd use an RCTFont helper to create a new font based on the existing font but with a scaleMultiplier of 1. However, the font didn't store the font size (12) and scaleMultiplier (1.5) separately. It just knows the (effective) font size of 18. So RCTFont thinks the new font has a font size of 18 and a scaleMultiplier of 1 so its effective font size is 18. This is incorrect and it should have been 12.
To fix this, the font attributes are now all stored individually. Anytime one of them changes, updateFont is called which recreates the font from scratch. This happens to fix some bugs around fontStyle and fontWeight which were reported several times before: #13730, #12738, #2140, #8533.
Created a test app where I verified that `allowFontScaling` works properly for `TextInputs` for all values (`undefined`, `true`, `false`) for a variety of `TextInputs`:
- Singleline TextInput
- Singleline TextInput's placeholder
- Multiline TextInput
- Multiline TextInput's placeholder
- Multiline TextInput using children instead of `value`
Also, verified switching `fontSize`, `fontWeight`, `fontStyle` and `fontFamily` through a bunch of combinations works properly.
Lastly, my team has been using this change in our app.
Adam Comella
Microsoft Corp.
Closes https://github.com/facebook/react-native/pull/14030
Reviewed By: TheSavior
Differential Revision: D5899959
Pulled By: shergin
fbshipit-source-id: c8c8c4d4d670cd2a142286e79bfffef3b58cecd3
2017-10-02 04:40:57 +00:00
|
|
|
#import <React/RCTUtils.h>
|
2017-06-27 23:05:05 +00:00
|
|
|
#import <React/UIView+React.h>
|
|
|
|
|
2017-07-18 21:33:33 +00:00
|
|
|
#import "RCTTextSelection.h"
|
|
|
|
|
2017-06-27 23:05:08 +00:00
|
|
|
@implementation RCTTextInput {
|
|
|
|
CGSize _previousContentSize;
|
2017-10-09 04:41:38 +00:00
|
|
|
BOOL _hasInputAccesoryView;
|
2017-06-27 23:05:08 +00:00
|
|
|
}
|
2017-06-27 23:05:05 +00:00
|
|
|
|
|
|
|
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
|
|
|
{
|
|
|
|
RCTAssertParam(bridge);
|
|
|
|
|
|
|
|
if (self = [super initWithFrame:CGRectZero]) {
|
|
|
|
_bridge = bridge;
|
|
|
|
_eventDispatcher = bridge.eventDispatcher;
|
iOS: Support allowFontScaling on TextInput
Summary:
Currently, only `Text` supports the `allowFontScaling` prop. This commit adds support for it on `TextInput`.
As part of this change, the TextInput setters for font attributes (e.g. size, weight) had to be refactored. The problem with them is that they use RCTFont's helpers which create a new font based on an existing font. These helpers lose information. In particular, they lose the scaleMultiplier.
For example, suppose the font size is 12 and the device's font multiplier is set to 1.5. So we'd create a font with size 12 and scaleMultiplier 1.5 which is an effective size of 18 (which is the only thing stored in the font). Next, suppose the device's font multiplier changes to 1. So we'd use an RCTFont helper to create a new font based on the existing font but with a scaleMultiplier of 1. However, the font didn't store the font size (12) and scaleMultiplier (1.5) separately. It just knows the (effective) font size of 18. So RCTFont thinks the new font has a font size of 18 and a scaleMultiplier of 1 so its effective font size is 18. This is incorrect and it should have been 12.
To fix this, the font attributes are now all stored individually. Anytime one of them changes, updateFont is called which recreates the font from scratch. This happens to fix some bugs around fontStyle and fontWeight which were reported several times before: #13730, #12738, #2140, #8533.
Created a test app where I verified that `allowFontScaling` works properly for `TextInputs` for all values (`undefined`, `true`, `false`) for a variety of `TextInputs`:
- Singleline TextInput
- Singleline TextInput's placeholder
- Multiline TextInput
- Multiline TextInput's placeholder
- Multiline TextInput using children instead of `value`
Also, verified switching `fontSize`, `fontWeight`, `fontStyle` and `fontFamily` through a bunch of combinations works properly.
Lastly, my team has been using this change in our app.
Adam Comella
Microsoft Corp.
Closes https://github.com/facebook/react-native/pull/14030
Reviewed By: TheSavior
Differential Revision: D5899959
Pulled By: shergin
fbshipit-source-id: c8c8c4d4d670cd2a142286e79bfffef3b58cecd3
2017-10-02 04:40:57 +00:00
|
|
|
_fontAttributes = [[RCTFontAttributes alloc] initWithAccessibilityManager:bridge.accessibilityManager];
|
|
|
|
_fontAttributes.delegate = self;
|
2017-06-27 23:05:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|
|
|
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)decoder)
|
|
|
|
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
|
|
|
|
|
|
|
- (id<RCTBackedTextInputViewProtocol>)backedTextInputView
|
|
|
|
{
|
|
|
|
RCTAssert(NO, @"-[RCTTextInput backedTextInputView] must be implemented in subclass.");
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
iOS: Support allowFontScaling on TextInput
Summary:
Currently, only `Text` supports the `allowFontScaling` prop. This commit adds support for it on `TextInput`.
As part of this change, the TextInput setters for font attributes (e.g. size, weight) had to be refactored. The problem with them is that they use RCTFont's helpers which create a new font based on an existing font. These helpers lose information. In particular, they lose the scaleMultiplier.
For example, suppose the font size is 12 and the device's font multiplier is set to 1.5. So we'd create a font with size 12 and scaleMultiplier 1.5 which is an effective size of 18 (which is the only thing stored in the font). Next, suppose the device's font multiplier changes to 1. So we'd use an RCTFont helper to create a new font based on the existing font but with a scaleMultiplier of 1. However, the font didn't store the font size (12) and scaleMultiplier (1.5) separately. It just knows the (effective) font size of 18. So RCTFont thinks the new font has a font size of 18 and a scaleMultiplier of 1 so its effective font size is 18. This is incorrect and it should have been 12.
To fix this, the font attributes are now all stored individually. Anytime one of them changes, updateFont is called which recreates the font from scratch. This happens to fix some bugs around fontStyle and fontWeight which were reported several times before: #13730, #12738, #2140, #8533.
Created a test app where I verified that `allowFontScaling` works properly for `TextInputs` for all values (`undefined`, `true`, `false`) for a variety of `TextInputs`:
- Singleline TextInput
- Singleline TextInput's placeholder
- Multiline TextInput
- Multiline TextInput's placeholder
- Multiline TextInput using children instead of `value`
Also, verified switching `fontSize`, `fontWeight`, `fontStyle` and `fontFamily` through a bunch of combinations works properly.
Lastly, my team has been using this change in our app.
Adam Comella
Microsoft Corp.
Closes https://github.com/facebook/react-native/pull/14030
Reviewed By: TheSavior
Differential Revision: D5899959
Pulled By: shergin
fbshipit-source-id: c8c8c4d4d670cd2a142286e79bfffef3b58cecd3
2017-10-02 04:40:57 +00:00
|
|
|
- (void)setFont:(UIFont *)font
|
|
|
|
{
|
|
|
|
self.backedTextInputView.font = font;
|
|
|
|
[self invalidateContentSize];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)fontAttributesDidChangeWithFont:(UIFont *)font
|
|
|
|
{
|
|
|
|
self.font = font;
|
|
|
|
}
|
|
|
|
|
2017-06-27 23:05:08 +00:00
|
|
|
#pragma mark - Properties
|
|
|
|
|
|
|
|
- (void)setReactPaddingInsets:(UIEdgeInsets)reactPaddingInsets
|
|
|
|
{
|
|
|
|
_reactPaddingInsets = reactPaddingInsets;
|
|
|
|
// We apply `paddingInsets` as `backedTextInputView`'s `textContainerInset`.
|
|
|
|
self.backedTextInputView.textContainerInset = reactPaddingInsets;
|
|
|
|
[self setNeedsLayout];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setReactBorderInsets:(UIEdgeInsets)reactBorderInsets
|
|
|
|
{
|
|
|
|
_reactBorderInsets = reactBorderInsets;
|
|
|
|
// We apply `borderInsets` as `backedTextInputView` layout offset.
|
|
|
|
self.backedTextInputView.frame = UIEdgeInsetsInsetRect(self.bounds, reactBorderInsets);
|
|
|
|
[self setNeedsLayout];
|
|
|
|
}
|
|
|
|
|
2017-07-18 21:33:45 +00:00
|
|
|
- (RCTTextSelection *)selection
|
|
|
|
{
|
|
|
|
id<RCTBackedTextInputViewProtocol> backedTextInput = self.backedTextInputView;
|
|
|
|
UITextRange *selectedTextRange = backedTextInput.selectedTextRange;
|
|
|
|
return [[RCTTextSelection new] initWithStart:[backedTextInput offsetFromPosition:backedTextInput.beginningOfDocument toPosition:selectedTextRange.start]
|
|
|
|
end:[backedTextInput offsetFromPosition:backedTextInput.beginningOfDocument toPosition:selectedTextRange.end]];
|
|
|
|
}
|
|
|
|
|
2017-07-18 21:33:33 +00:00
|
|
|
- (void)setSelection:(RCTTextSelection *)selection
|
|
|
|
{
|
|
|
|
if (!selection) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
id<RCTBackedTextInputViewProtocol> backedTextInput = self.backedTextInputView;
|
|
|
|
|
2017-07-18 21:33:45 +00:00
|
|
|
UITextRange *previousSelectedTextRange = backedTextInput.selectedTextRange;
|
2017-07-18 21:33:33 +00:00
|
|
|
UITextPosition *start = [backedTextInput positionFromPosition:backedTextInput.beginningOfDocument offset:selection.start];
|
|
|
|
UITextPosition *end = [backedTextInput positionFromPosition:backedTextInput.beginningOfDocument offset:selection.end];
|
|
|
|
UITextRange *selectedTextRange = [backedTextInput textRangeFromPosition:start toPosition:end];
|
|
|
|
|
|
|
|
NSInteger eventLag = _nativeEventCount - _mostRecentEventCount;
|
2017-07-18 21:33:45 +00:00
|
|
|
if (eventLag == 0 && ![previousSelectedTextRange isEqual:selectedTextRange]) {
|
|
|
|
[backedTextInput setSelectedTextRange:selectedTextRange notifyDelegate:NO];
|
2017-07-18 21:33:33 +00:00
|
|
|
} else if (eventLag > RCTTextUpdateLagWarningThreshold) {
|
2017-09-25 17:23:02 +00:00
|
|
|
RCTLogWarn(@"Native TextInput(%@) is %lld events ahead of JS - try to make your JS faster.", backedTextInput.text, (long long)eventLag);
|
2017-07-18 21:33:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-18 21:33:35 +00:00
|
|
|
#pragma mark - RCTBackedTextInputDelegate
|
|
|
|
|
2017-07-18 21:33:37 +00:00
|
|
|
- (BOOL)textInputShouldBeginEditing
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2017-07-18 21:33:41 +00:00
|
|
|
- (void)textInputDidBeginEditing
|
|
|
|
{
|
|
|
|
if (_clearTextOnFocus) {
|
|
|
|
self.backedTextInputView.text = @"";
|
|
|
|
}
|
|
|
|
|
2017-07-18 21:33:54 +00:00
|
|
|
if (_selectTextOnFocus) {
|
|
|
|
[self.backedTextInputView selectAll:nil];
|
|
|
|
}
|
|
|
|
|
2017-07-18 21:33:41 +00:00
|
|
|
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus
|
|
|
|
reactTag:self.reactTag
|
|
|
|
text:self.backedTextInputView.text
|
|
|
|
key:nil
|
|
|
|
eventCount:_nativeEventCount];
|
|
|
|
}
|
|
|
|
|
2017-07-18 21:33:35 +00:00
|
|
|
- (BOOL)textInputShouldReturn
|
|
|
|
{
|
2017-08-11 01:09:35 +00:00
|
|
|
// We send `submit` event here, in `textInputShouldReturn`
|
|
|
|
// (not in `textInputDidReturn)`, because of semantic of the event:
|
|
|
|
// `onSubmitEditing` is called when "Submit" button
|
|
|
|
// (the blue key on onscreen keyboard) did pressed
|
|
|
|
// (no connection to any specific "submitting" process).
|
2017-07-18 21:33:35 +00:00
|
|
|
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeSubmit
|
|
|
|
reactTag:self.reactTag
|
|
|
|
text:self.backedTextInputView.text
|
|
|
|
key:nil
|
|
|
|
eventCount:_nativeEventCount];
|
2017-08-11 01:09:35 +00:00
|
|
|
|
|
|
|
return _blurOnSubmit;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)textInputDidReturn
|
|
|
|
{
|
|
|
|
// Does nothing.
|
2017-07-18 21:33:35 +00:00
|
|
|
}
|
|
|
|
|
2017-07-18 21:33:45 +00:00
|
|
|
- (void)textInputDidChangeSelection
|
|
|
|
{
|
|
|
|
if (!_onSelectionChange) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
RCTTextSelection *selection = self.selection;
|
|
|
|
_onSelectionChange(@{
|
|
|
|
@"selection": @{
|
|
|
|
@"start": @(selection.start),
|
|
|
|
@"end": @(selection.end),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-07-18 21:33:47 +00:00
|
|
|
- (BOOL)textInputShouldEndEditing
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)textInputDidEndEditing
|
|
|
|
{
|
|
|
|
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeEnd
|
|
|
|
reactTag:self.reactTag
|
|
|
|
text:self.backedTextInputView.text
|
|
|
|
key:nil
|
|
|
|
eventCount:_nativeEventCount];
|
|
|
|
|
|
|
|
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeBlur
|
|
|
|
reactTag:self.reactTag
|
|
|
|
text:self.backedTextInputView.text
|
|
|
|
key:nil
|
|
|
|
eventCount:_nativeEventCount];
|
|
|
|
}
|
|
|
|
|
2017-06-27 23:05:08 +00:00
|
|
|
#pragma mark - Content Size (in Yoga terms, without any insets)
|
|
|
|
|
|
|
|
- (CGSize)contentSize
|
|
|
|
{
|
2017-07-18 21:33:58 +00:00
|
|
|
CGSize contentSize = self.backedTextInputView.contentSize;
|
|
|
|
UIEdgeInsets reactPaddingInsets = self.reactPaddingInsets;
|
|
|
|
contentSize.width -= reactPaddingInsets.left + reactPaddingInsets.right;
|
|
|
|
contentSize.height -= reactPaddingInsets.top + reactPaddingInsets.bottom;
|
2017-06-27 23:05:08 +00:00
|
|
|
// Returning value does NOT include border and padding insets.
|
|
|
|
return contentSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)invalidateContentSize
|
|
|
|
{
|
|
|
|
// Updates `contentSize` property and notifies Yoga about the change, if necessary.
|
|
|
|
CGSize contentSize = self.contentSize;
|
|
|
|
|
|
|
|
if (CGSizeEqualToSize(_previousContentSize, contentSize)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_previousContentSize = contentSize;
|
|
|
|
|
|
|
|
[_bridge.uiManager setIntrinsicContentSize:contentSize forView:self];
|
|
|
|
|
|
|
|
if (_onContentSizeChange) {
|
|
|
|
_onContentSizeChange(@{
|
|
|
|
@"contentSize": @{
|
|
|
|
@"height": @(contentSize.height),
|
|
|
|
@"width": @(contentSize.width),
|
|
|
|
},
|
|
|
|
@"target": self.reactTag,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Layout (in UIKit terms, with all insets)
|
|
|
|
|
|
|
|
- (CGSize)intrinsicContentSize
|
|
|
|
{
|
|
|
|
CGSize size = self.backedTextInputView.intrinsicContentSize;
|
|
|
|
size.width += _reactBorderInsets.left + _reactBorderInsets.right;
|
|
|
|
size.height += _reactBorderInsets.top + _reactBorderInsets.bottom;
|
|
|
|
// Returning value DOES include border and padding insets.
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (CGSize)sizeThatFits:(CGSize)size
|
|
|
|
{
|
|
|
|
CGFloat compoundHorizontalBorderInset = _reactBorderInsets.left + _reactBorderInsets.right;
|
|
|
|
CGFloat compoundVerticalBorderInset = _reactBorderInsets.top + _reactBorderInsets.bottom;
|
|
|
|
|
|
|
|
size.width -= compoundHorizontalBorderInset;
|
|
|
|
size.height -= compoundVerticalBorderInset;
|
|
|
|
|
|
|
|
// Note: `paddingInsets` was already included in `backedTextInputView` size
|
|
|
|
// because it was applied as `textContainerInset`.
|
|
|
|
CGSize fittingSize = [self.backedTextInputView sizeThatFits:size];
|
|
|
|
|
|
|
|
fittingSize.width += compoundHorizontalBorderInset;
|
|
|
|
fittingSize.height += compoundVerticalBorderInset;
|
|
|
|
|
|
|
|
// Returning value DOES include border and padding insets.
|
|
|
|
return fittingSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)layoutSubviews
|
|
|
|
{
|
|
|
|
[super layoutSubviews];
|
|
|
|
[self invalidateContentSize];
|
|
|
|
}
|
|
|
|
|
2017-06-27 23:05:05 +00:00
|
|
|
#pragma mark - Accessibility
|
|
|
|
|
2017-07-20 00:12:04 +00:00
|
|
|
- (UIView *)reactAccessibilityElement
|
2017-06-27 23:05:05 +00:00
|
|
|
{
|
|
|
|
return self.backedTextInputView;
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark - Focus Control
|
|
|
|
|
|
|
|
- (void)reactFocus
|
|
|
|
{
|
|
|
|
[self.backedTextInputView reactFocus];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)reactBlur
|
|
|
|
{
|
|
|
|
[self.backedTextInputView reactBlur];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)didMoveToWindow
|
|
|
|
{
|
|
|
|
[self.backedTextInputView reactFocusIfNeeded];
|
|
|
|
}
|
|
|
|
|
2017-06-27 23:05:11 +00:00
|
|
|
#pragma mark - Custom Input Accessory View
|
|
|
|
|
|
|
|
- (void)didSetProps:(NSArray<NSString *> *)changedProps
|
|
|
|
{
|
|
|
|
[self invalidateInputAccessoryView];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)invalidateInputAccessoryView
|
|
|
|
{
|
|
|
|
#if !TARGET_OS_TV
|
|
|
|
UIView<RCTBackedTextInputViewProtocol> *textInputView = self.backedTextInputView;
|
|
|
|
UIKeyboardType keyboardType = textInputView.keyboardType;
|
|
|
|
|
|
|
|
// These keyboard types (all are number pads) don't have a "Done" button by default,
|
|
|
|
// so we create an `inputAccessoryView` with this button for them.
|
|
|
|
BOOL shouldHaveInputAccesoryView =
|
|
|
|
(
|
|
|
|
keyboardType == UIKeyboardTypeNumberPad ||
|
|
|
|
keyboardType == UIKeyboardTypePhonePad ||
|
|
|
|
keyboardType == UIKeyboardTypeDecimalPad ||
|
|
|
|
keyboardType == UIKeyboardTypeASCIICapableNumberPad
|
|
|
|
) &&
|
|
|
|
textInputView.returnKeyType == UIReturnKeyDone;
|
|
|
|
|
2017-10-09 04:41:38 +00:00
|
|
|
if (_hasInputAccesoryView == shouldHaveInputAccesoryView) {
|
2017-06-27 23:05:11 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-10-09 04:41:38 +00:00
|
|
|
_hasInputAccesoryView = shouldHaveInputAccesoryView;
|
|
|
|
|
2017-06-27 23:05:11 +00:00
|
|
|
if (shouldHaveInputAccesoryView) {
|
|
|
|
UIToolbar *toolbarView = [[UIToolbar alloc] init];
|
|
|
|
[toolbarView sizeToFit];
|
|
|
|
UIBarButtonItem *flexibleSpace =
|
|
|
|
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
|
|
|
|
target:nil
|
|
|
|
action:nil];
|
|
|
|
UIBarButtonItem *doneButton =
|
|
|
|
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
|
|
|
|
target:self
|
|
|
|
action:@selector(handleInputAccessoryDoneButton)];
|
|
|
|
toolbarView.items = @[flexibleSpace, doneButton];
|
|
|
|
textInputView.inputAccessoryView = toolbarView;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
textInputView.inputAccessoryView = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have to call `reloadInputViews` for focused text inputs to update an accessory view.
|
|
|
|
if (textInputView.isFirstResponder) {
|
|
|
|
[textInputView reloadInputViews];
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)handleInputAccessoryDoneButton
|
|
|
|
{
|
2017-08-10 10:24:06 +00:00
|
|
|
if ([self textInputShouldReturn]) {
|
|
|
|
[self.backedTextInputView endEditing:YES];
|
|
|
|
}
|
2017-06-27 23:05:11 +00:00
|
|
|
}
|
|
|
|
|
2017-06-27 23:05:05 +00:00
|
|
|
@end
|