@@ -38,6 +38,8 @@ @interface SLKTextViewController ()
38
38
// A hairline displayed on top of the auto-completion view, to better separate the content from the control.
39
39
@property (nonatomic , strong ) UIView *autoCompletionHairline;
40
40
41
+ @property (nonatomic , strong ) SLKInputAccessoryView *inputAccessoryView;
42
+
41
43
// Auto-Layout height constraints used for updating their constants
42
44
@property (nonatomic , strong ) NSLayoutConstraint *scrollViewHC;
43
45
@property (nonatomic , strong ) NSLayoutConstraint *textInputbarHC;
@@ -372,19 +374,20 @@ - (UIButton *)rightButton
372
374
373
375
- (SLKInputAccessoryView *)emptyInputAccessoryView
374
376
{
375
- if (!self.isKeyboardPanningEnabled ) {
376
- return nil ;
377
- }
378
-
379
- SLKInputAccessoryView *view = [[SLKInputAccessoryView alloc ] initWithFrame: self .textInputbar.bounds];
380
- view.backgroundColor = [UIColor clearColor ];
381
- view.userInteractionEnabled = NO ;
382
-
377
+ if (self.inputAccessoryView == nil )
378
+ {
379
+ SLKInputAccessoryView *view = [[SLKInputAccessoryView alloc ] initWithFrame: self .textInputbar.bounds];
380
+ view.backgroundColor = [UIColor clearColor ];
381
+ view.userInteractionEnabled = NO ;
382
+
383
383
#if SLK_INPUT_ACCESSORY_DEBUG
384
- view.backgroundColor = [[UIColor redColor ] colorWithAlphaComponent: 0.5 ];
384
+ view.backgroundColor = [[UIColor redColor ] colorWithAlphaComponent: 0.5 ];
385
385
#endif
386
+
387
+ self.inputAccessoryView = view;
388
+ }
386
389
387
- return view ;
390
+ return self. inputAccessoryView ;
388
391
}
389
392
390
393
- (UIModalPresentationStyle)modalPresentationStyle
@@ -506,6 +509,8 @@ - (void)setScrollViewProxy:(UIScrollView *)scrollView
506
509
507
510
[scrollView addGestureRecognizer: self .singleTapGesture];
508
511
512
+ [scrollView.panGestureRecognizer addTarget: self action: @selector (slk_didPanScrollView: )];
513
+
509
514
_scrollViewProxy = scrollView;
510
515
}
511
516
@@ -532,17 +537,6 @@ - (void)setInverted:(BOOL)inverted
532
537
self.automaticallyAdjustsScrollViewInsets = inverted ? NO : YES ;
533
538
}
534
539
535
- - (void )setKeyboardPanningEnabled : (BOOL )enabled
536
- {
537
- if (_keyboardPanningEnabled == enabled) {
538
- return ;
539
- }
540
-
541
- _keyboardPanningEnabled = enabled;
542
-
543
- self.scrollViewProxy .keyboardDismissMode = enabled ? UIScrollViewKeyboardDismissModeInteractive : UIScrollViewKeyboardDismissModeNone;
544
- }
545
-
546
540
- (BOOL )slk_updateKeyboardStatus : (SLKKeyboardStatus)status
547
541
{
548
542
// Skips if trying to update the same status
@@ -817,6 +811,127 @@ - (void)willRequestUndo
817
811
818
812
#pragma mark - Private Methods
819
813
814
+ - (void )slk_didPanScrollView : (UIPanGestureRecognizer *)gesture
815
+ {
816
+ // if the panning is not enable, then let's skip it..
817
+ if (!self.keyboardPanningEnabled ) return ;
818
+
819
+ // Skips this if it's not the expected textView.
820
+ // Checking the keyboard height constant helps to disable the view constraints update on iPad when the keyboard is undocked.
821
+ // Checking the keyboard status allows to keep the inputAccessoryView valid when still reacing the bottom of the screen.
822
+ if (![self .textView isFirstResponder ] || (self.keyboardHC .constant == 0 && self.keyboardStatus == SLKKeyboardStatusDidHide)) {
823
+ return ;
824
+ }
825
+
826
+ // Skips if the view isn't visible
827
+ if (!self.view .window ) {
828
+ return ;
829
+ }
830
+
831
+ // Skips if it is presented inside of a popover
832
+ if (self.isPresentedInPopover ) {
833
+ return ;
834
+ }
835
+
836
+ static CGPoint startPoint = (CGPoint ){0 ,0 };
837
+ static BOOL dragging = NO ;
838
+ static CGRect originalFrame = (CGRect ){{0 ,0 },{0 ,0 }};
839
+
840
+ CGPoint point = [gesture locationInView: self .view];
841
+
842
+ if (gesture.state == UIGestureRecognizerStateBegan)
843
+ {
844
+ startPoint = CGPointZero ;
845
+ dragging = NO ;
846
+ }
847
+ else if (gesture.state == UIGestureRecognizerStateChanged)
848
+ {
849
+ if (CGRectContainsPoint (self.textInputbar .frame , point) || dragging)
850
+ {
851
+ if (CGPointEqualToPoint (startPoint, CGPointZero )) {
852
+ startPoint = point;
853
+ dragging = YES ;
854
+ originalFrame = self.inputAccessoryView .keyboard .frame ;
855
+ }
856
+
857
+ self.movingKeyboard = YES ;
858
+
859
+ CGPoint transition = CGPointMake (point.x - startPoint.x , point.y - startPoint.y );
860
+
861
+ CGRect keyboardFrame = originalFrame;
862
+
863
+ keyboardFrame.origin .y += MAX (transition.y ,0 );
864
+
865
+ self.inputAccessoryView .keyboard .frame = keyboardFrame;
866
+
867
+ CGFloat constant = MAX (0.0 , CGRectGetHeight (self.view .bounds ) - CGRectGetMinY (keyboardFrame) - CGRectGetHeight (self.textView .inputAccessoryView .bounds ));
868
+
869
+ self.keyboardHC .constant = constant;
870
+ self.scrollViewHC .constant = [self slk_appropriateScrollViewHeight ];
871
+
872
+ // layoutIfNeeded must be called before any further scrollView internal adjustments (content offset and size)
873
+ [self .view layoutIfNeeded ];
874
+
875
+ // Overrides the scrollView's contentOffset to allow following the same position when dragging the keyboard
876
+ CGPoint offset = _scrollViewOffsetBeforeDragging;
877
+
878
+ if (self.isInverted )
879
+ {
880
+ if (!self.scrollViewProxy .isDecelerating && self.scrollViewProxy .isTracking ) {
881
+ self.scrollViewProxy .contentOffset = _scrollViewOffsetBeforeDragging;
882
+ }
883
+ }
884
+ else
885
+ {
886
+ CGFloat keyboardHeightDelta = _keyboardHeightBeforeDragging-self.keyboardHC .constant ;
887
+ offset.y -= keyboardHeightDelta;
888
+
889
+ self.scrollViewProxy .contentOffset = offset;
890
+ }
891
+ }
892
+
893
+ }
894
+ else if (gesture.state == UIGestureRecognizerStateCancelled ||
895
+ gesture.state == UIGestureRecognizerStateEnded ||
896
+ gesture.state == UIGestureRecognizerStateFailed )
897
+ {
898
+ if (dragging)
899
+ {
900
+ CGPoint transition = CGPointMake (point.x - startPoint.x , point.y - startPoint.y );
901
+
902
+ if (transition.y < 0 ) transition.y = 0 ;
903
+
904
+ startPoint = CGPointZero ;
905
+ dragging = NO ;
906
+ CGRect keyboardFrame = originalFrame;
907
+
908
+ // the velocity can be changed to hide or show the keyboard based on the gesture
909
+ CGFloat minVelocity = 20 .0f ;
910
+ BOOL hide = [gesture velocityInView: self .view].y > minVelocity || transition.y > keyboardFrame.size .height /2 .0f ;
911
+
912
+ if (hide) keyboardFrame.origin .y = [UIScreen mainScreen ].bounds .size .height ;
913
+
914
+ CGFloat constant = MAX (0.0 , CGRectGetHeight (self.view .bounds ) - CGRectGetMinY (keyboardFrame) - CGRectGetHeight (self.textView .inputAccessoryView .bounds ));
915
+
916
+ self.keyboardHC .constant = constant;
917
+ self.scrollViewHC .constant = [self slk_appropriateScrollViewHeight ];
918
+
919
+ [UIView animateWithDuration: 0.15 animations: ^{
920
+ [self .view layoutIfNeeded ];
921
+ self.inputAccessoryView .keyboard .frame = keyboardFrame;
922
+ } completion: ^(BOOL finished) {
923
+ if (hide)
924
+ {
925
+ [UIView performWithoutAnimation: ^{
926
+ [self .textView resignFirstResponder ];
927
+ }];
928
+ }
929
+ }];
930
+ }
931
+
932
+ }
933
+ }
934
+
820
935
- (void )slk_didTapScrollView : (UIGestureRecognizer *)gesture
821
936
{
822
937
if (!self.isPresentedInPopover && !self.isExternalKeyboardDetected ) {
@@ -1173,55 +1288,6 @@ - (void)slk_didShowOrHideKeyboard:(NSNotification *)notification
1173
1288
self.movingKeyboard = NO ;
1174
1289
}
1175
1290
1176
- - (void )slk_didChangeKeyboardFrame : (NSNotification *)notification
1177
- {
1178
- // Skips if the view isn't visible
1179
- if (!self.view .window ) {
1180
- return ;
1181
- }
1182
-
1183
- // Skips if it is presented inside of a popover
1184
- if (self.isPresentedInPopover ) {
1185
- return ;
1186
- }
1187
-
1188
- // Skips this if it's not the expected textView.
1189
- // Checking the keyboard height constant helps to disable the view constraints update on iPad when the keyboard is undocked.
1190
- // Checking the keyboard status allows to keep the inputAccessoryView valid when still reacing the bottom of the screen.
1191
- if (![self .textView isFirstResponder ] || (self.keyboardHC .constant == 0 && self.keyboardStatus == SLKKeyboardStatusDidHide)) {
1192
- return ;
1193
- }
1194
-
1195
- if (self.scrollViewProxy .isDragging ) {
1196
- self.movingKeyboard = YES ;
1197
- }
1198
-
1199
- if (self.isMovingKeyboard == NO ) {
1200
- return ;
1201
- }
1202
-
1203
- self.keyboardHC .constant = [self slk_appropriateKeyboardHeight: notification];
1204
- self.scrollViewHC .constant = [self slk_appropriateScrollViewHeight ];
1205
-
1206
- // layoutIfNeeded must be called before any further scrollView internal adjustments (content offset and size)
1207
- [self .view layoutIfNeeded ];
1208
-
1209
- // Overrides the scrollView's contentOffset to allow following the same position when dragging the keyboard
1210
- CGPoint offset = _scrollViewOffsetBeforeDragging;
1211
-
1212
- if (self.isInverted ) {
1213
- if (!self.scrollViewProxy .isDecelerating && self.scrollViewProxy .isTracking ) {
1214
- self.scrollViewProxy .contentOffset = _scrollViewOffsetBeforeDragging;
1215
- }
1216
- }
1217
- else {
1218
- CGFloat keyboardHeightDelta = _keyboardHeightBeforeDragging-self.keyboardHC .constant ;
1219
- offset.y -= keyboardHeightDelta;
1220
-
1221
- self.scrollViewProxy .contentOffset = offset;
1222
- }
1223
- }
1224
-
1225
1291
- (void )slk_didPostSLKKeyboardNotification : (NSNotification *)notification
1226
1292
{
1227
1293
if (![notification.object isEqual: self .textView]) {
@@ -1865,8 +1931,6 @@ - (void)slk_registerNotifications
1865
1931
[[NSNotificationCenter defaultCenter ] addObserver: self selector: @selector (slk_didPostSLKKeyboardNotification: ) name: SLKKeyboardDidHideNotification object: nil ];
1866
1932
#endif
1867
1933
1868
- // Keyboard Accessory View notifications
1869
- [[NSNotificationCenter defaultCenter ] addObserver: self selector: @selector (slk_didChangeKeyboardFrame: ) name: SLKInputAccessoryViewKeyboardFrameDidChangeNotification object: nil ];
1870
1934
1871
1935
// TextView notifications
1872
1936
[[NSNotificationCenter defaultCenter ] addObserver: self selector: @selector (slk_willChangeTextViewText: ) name: SLKTextViewTextWillChangeNotification object: nil ];
@@ -1897,9 +1961,6 @@ - (void)slk_unregisterNotifications
1897
1961
[[NSNotificationCenter defaultCenter ] removeObserver: self name: SLKKeyboardDidHideNotification object: nil ];
1898
1962
#endif
1899
1963
1900
- // TextView notifications
1901
- [[NSNotificationCenter defaultCenter ] removeObserver: self name: SLKInputAccessoryViewKeyboardFrameDidChangeNotification object: nil ];
1902
-
1903
1964
// TextView notifications
1904
1965
[[NSNotificationCenter defaultCenter ] removeObserver: self name: UITextViewTextDidBeginEditingNotification object: nil ];
1905
1966
[[NSNotificationCenter defaultCenter ] removeObserver: self name: UITextViewTextDidEndEditingNotification object: nil ];
0 commit comments