From 83b9d5c682b21d88133f24da0f94dd56bd79e687 Mon Sep 17 00:00:00 2001 From: 单军华 Date: Thu, 19 Jul 2018 13:38:55 +0800 Subject: [PATCH] change --- screendisplay/Pods/YYText/YYText/Component/YYTextKeyboardManager.m | 521 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 521 insertions(+), 0 deletions(-) diff --git a/screendisplay/Pods/YYText/YYText/Component/YYTextKeyboardManager.m b/screendisplay/Pods/YYText/YYText/Component/YYTextKeyboardManager.m new file mode 100755 index 0000000..43467bb --- /dev/null +++ b/screendisplay/Pods/YYText/YYText/Component/YYTextKeyboardManager.m @@ -0,0 +1,521 @@ +// +// YYTextKeyboardManager.m +// YYText <https://github.com/ibireme/YYText> +// +// Created by ibireme on 15/6/3. +// Copyright (c) 2015 ibireme. +// +// This source code is licensed under the MIT-style license found in the +// LICENSE file in the root directory of this source tree. +// + +#import "YYTextKeyboardManager.h" +#import "YYTextUtilities.h" +#import <objc/runtime.h> + + +static int _YYTextKeyboardViewFrameObserverKey; + +/// Observer for view's frame/bounds/center/transform +@interface _YYTextKeyboardViewFrameObserver : NSObject +@property (nonatomic, copy) void (^notifyBlock)(UIView *keyboard); +- (void)addToKeyboardView:(UIView *)keyboardView; ++ (instancetype)observerForView:(UIView *)keyboardView; +@end + + +@implementation _YYTextKeyboardViewFrameObserver { + __unsafe_unretained UIView *_keyboardView; +} +- (void)addToKeyboardView:(UIView *)keyboardView { + if (_keyboardView == keyboardView) return; + if (_keyboardView) { + [self removeFrameObserver]; + objc_setAssociatedObject(_keyboardView, &_YYTextKeyboardViewFrameObserverKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + _keyboardView = keyboardView; + if (keyboardView) { + [self addFrameObserver]; + } + objc_setAssociatedObject(keyboardView, &_YYTextKeyboardViewFrameObserverKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void)removeFrameObserver { + [_keyboardView removeObserver:self forKeyPath:@"frame"]; + [_keyboardView removeObserver:self forKeyPath:@"center"]; + [_keyboardView removeObserver:self forKeyPath:@"bounds"]; + [_keyboardView removeObserver:self forKeyPath:@"transform"]; + _keyboardView = nil; +} + +- (void)addFrameObserver { + if (!_keyboardView) return; + [_keyboardView addObserver:self forKeyPath:@"frame" options:kNilOptions context:NULL]; + [_keyboardView addObserver:self forKeyPath:@"center" options:kNilOptions context:NULL]; + [_keyboardView addObserver:self forKeyPath:@"bounds" options:kNilOptions context:NULL]; + [_keyboardView addObserver:self forKeyPath:@"transform" options:kNilOptions context:NULL]; +} + +- (void)dealloc { + [self removeFrameObserver]; +} + ++ (instancetype)observerForView:(UIView *)keyboardView { + if (!keyboardView) return nil; + return objc_getAssociatedObject(keyboardView, &_YYTextKeyboardViewFrameObserverKey); +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + + BOOL isPrior = [[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue]; + if (isPrior) return; + + NSKeyValueChange changeKind = [[change objectForKey:NSKeyValueChangeKindKey] integerValue]; + if (changeKind != NSKeyValueChangeSetting) return; + + id newVal = [change objectForKey:NSKeyValueChangeNewKey]; + if (newVal == [NSNull null]) newVal = nil; + + if (_notifyBlock) { + _notifyBlock(_keyboardView); + } +} + +@end + + + +@implementation YYTextKeyboardManager { + NSHashTable *_observers; + + CGRect _fromFrame; + BOOL _fromVisible; + UIInterfaceOrientation _fromOrientation; + + CGRect _notificationFromFrame; + CGRect _notificationToFrame; + NSTimeInterval _notificationDuration; + UIViewAnimationCurve _notificationCurve; + BOOL _hasNotification; + + CGRect _observedToFrame; + BOOL _hasObservedChange; + + BOOL _lastIsNotification; +} + +- (instancetype)init { + @throw [NSException exceptionWithName:@"YYTextKeyboardManager init error" reason:@"Use 'defaultManager' to get instance." userInfo:nil]; + return [super init]; +} + +- (instancetype)_init { + self = [super init]; + _observers = [[NSHashTable alloc] initWithOptions:NSPointerFunctionsWeakMemory|NSPointerFunctionsObjectPointerPersonality capacity:0]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_keyboardFrameWillChangeNotification:) + name:UIKeyboardWillChangeFrameNotification + object:nil]; + // for iPad (iOS 9) + if ([UIDevice currentDevice].systemVersion.floatValue >= 9) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_keyboardFrameDidChangeNotification:) + name:UIKeyboardDidChangeFrameNotification + object:nil]; + } + return self; +} + +- (void)_initFrameObserver { + UIView *keyboardView = self.keyboardView; + if (!keyboardView) return; + __weak typeof(self) _self = self; + _YYTextKeyboardViewFrameObserver *observer = [_YYTextKeyboardViewFrameObserver observerForView:keyboardView]; + if (!observer) { + observer = [_YYTextKeyboardViewFrameObserver new]; + observer.notifyBlock = ^(UIView *keyboard) { + [_self _keyboardFrameChanged:keyboard]; + }; + [observer addToKeyboardView:keyboardView]; + } +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + ++ (instancetype)defaultManager { + static YYTextKeyboardManager *mgr = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (!YYTextIsAppExtension()) { + mgr = [[self alloc] _init]; + } + }); + return mgr; +} + ++ (void)load { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self defaultManager]; + }); +} + +- (void)addObserver:(id<YYTextKeyboardObserver>)observer { + if (!observer) return; + [_observers addObject:observer]; +} + +- (void)removeObserver:(id<YYTextKeyboardObserver>)observer { + if (!observer) return; + [_observers removeObject:observer]; +} + +- (UIWindow *)keyboardWindow { + UIApplication *app = YYTextSharedApplication(); + if (!app) return nil; + + UIWindow *window = nil; + for (window in app.windows) { + if ([self _getKeyboardViewFromWindow:window]) return window; + } + window = app.keyWindow; + if ([self _getKeyboardViewFromWindow:window]) return window; + + NSMutableArray *kbWindows = nil; + for (window in app.windows) { + NSString *windowName = NSStringFromClass(window.class); + if ([self _systemVersion] < 9) { + // UITextEffectsWindow + if (windowName.length == 19 && + [windowName hasPrefix:@"UI"] && + [windowName hasSuffix:@"TextEffectsWindow"]) { + if (!kbWindows) kbWindows = [NSMutableArray new]; + [kbWindows addObject:window]; + } + } else { + // UIRemoteKeyboardWindow + if (windowName.length == 22 && + [windowName hasPrefix:@"UI"] && + [windowName hasSuffix:@"RemoteKeyboardWindow"]) { + if (!kbWindows) kbWindows = [NSMutableArray new]; + [kbWindows addObject:window]; + } + } + } + + if (kbWindows.count == 1) { + return kbWindows.firstObject; + } + return nil; +} + +- (UIView *)keyboardView { + UIApplication *app = YYTextSharedApplication(); + if (!app) return nil; + + UIWindow *window = nil; + UIView *view = nil; + for (window in app.windows) { + view = [self _getKeyboardViewFromWindow:window]; + if (view) return view; + } + window = app.keyWindow; + view = [self _getKeyboardViewFromWindow:window]; + if (view) return view; + return nil; +} + +- (BOOL)isKeyboardVisible { + UIWindow *window = self.keyboardWindow; + if (!window) return NO; + UIView *view = self.keyboardView; + if (!view) return NO; + CGRect rect = CGRectIntersection(window.bounds, view.frame); + if (CGRectIsNull(rect)) return NO; + if (CGRectIsInfinite(rect)) return NO; + return rect.size.width > 0 && rect.size.height > 0; +} + +- (CGRect)keyboardFrame { + UIView *keyboard = [self keyboardView]; + if (!keyboard) return CGRectNull; + + CGRect frame = CGRectNull; + UIWindow *window = keyboard.window; + if (window) { + frame = [window convertRect:keyboard.frame toWindow:nil]; + } else { + frame = keyboard.frame; + } + return frame; +} + +#pragma mark - private + +- (double)_systemVersion { + static double v; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + v = [UIDevice currentDevice].systemVersion.doubleValue; + }); + return v; +} + +- (UIView *)_getKeyboardViewFromWindow:(UIWindow *)window { + /* + iOS 6/7: + UITextEffectsWindow + UIPeripheralHostView << keyboard + + iOS 8: + UITextEffectsWindow + UIInputSetContainerView + UIInputSetHostView << keyboard + + iOS 9: + UIRemoteKeyboardWindow + UIInputSetContainerView + UIInputSetHostView << keyboard + */ + if (!window) return nil; + + // Get the window + NSString *windowName = NSStringFromClass(window.class); + if ([self _systemVersion] < 9) { + // UITextEffectsWindow + if (windowName.length != 19) return nil; + if (![windowName hasPrefix:@"UI"]) return nil; + if (![windowName hasSuffix:@"TextEffectsWindow"]) return nil; + } else { + // UIRemoteKeyboardWindow + if (windowName.length != 22) return nil; + if (![windowName hasPrefix:@"UI"]) return nil; + if (![windowName hasSuffix:@"RemoteKeyboardWindow"]) return nil; + } + + // Get the view + if ([self _systemVersion] < 8) { + // UIPeripheralHostView + for (UIView *view in window.subviews) { + NSString *viewName = NSStringFromClass(view.class); + if (viewName.length != 20) continue; + if (![viewName hasPrefix:@"UI"]) continue; + if (![viewName hasSuffix:@"PeripheralHostView"]) continue; + return view; + } + } else { + // UIInputSetContainerView + for (UIView *view in window.subviews) { + NSString *viewName = NSStringFromClass(view.class); + if (viewName.length != 23) continue; + if (![viewName hasPrefix:@"UI"]) continue; + if (![viewName hasSuffix:@"InputSetContainerView"]) continue; + // UIInputSetHostView + for (UIView *subView in view.subviews) { + NSString *subViewName = NSStringFromClass(subView.class); + if (subViewName.length != 18) continue; + if (![subViewName hasPrefix:@"UI"]) continue; + if (![subViewName hasSuffix:@"InputSetHostView"]) continue; + return subView; + } + } + } + + return nil; +} + +- (void)_keyboardFrameWillChangeNotification:(NSNotification *)notif { + if (![notif.name isEqualToString:UIKeyboardWillChangeFrameNotification]) return; + NSDictionary *info = notif.userInfo; + if (!info) return; + + [self _initFrameObserver]; + + NSValue *beforeValue = info[UIKeyboardFrameBeginUserInfoKey]; + NSValue *afterValue = info[UIKeyboardFrameEndUserInfoKey]; + NSNumber *curveNumber = info[UIKeyboardAnimationCurveUserInfoKey]; + NSNumber *durationNumber = info[UIKeyboardAnimationDurationUserInfoKey]; + + CGRect before = beforeValue.CGRectValue; + CGRect after = afterValue.CGRectValue; + UIViewAnimationCurve curve = curveNumber.integerValue; + NSTimeInterval duration = durationNumber.doubleValue; + + // ignore zero end frame + if (after.size.width <= 0 && after.size.height <= 0) return; + + _notificationFromFrame = before; + _notificationToFrame = after; + _notificationCurve = curve; + _notificationDuration = duration; + _hasNotification = YES; + _lastIsNotification = YES; + + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_notifyAllObservers) object:nil]; + if (duration == 0) { + [self performSelector:@selector(_notifyAllObservers) withObject:nil afterDelay:0 inModes:@[NSRunLoopCommonModes]]; + } else { + [self _notifyAllObservers]; + } +} + +- (void)_keyboardFrameDidChangeNotification:(NSNotification *)notif { + if (![notif.name isEqualToString:UIKeyboardDidChangeFrameNotification]) return; + NSDictionary *info = notif.userInfo; + if (!info) return; + + [self _initFrameObserver]; + + NSValue *afterValue = info[UIKeyboardFrameEndUserInfoKey]; + CGRect after = afterValue.CGRectValue; + + // ignore zero end frame + if (after.size.width <= 0 && after.size.height <= 0) return; + + _notificationToFrame = after; + _notificationCurve = UIViewAnimationCurveEaseInOut; + _notificationDuration = 0; + _hasNotification = YES; + _lastIsNotification = YES; + + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_notifyAllObservers) object:nil]; + [self performSelector:@selector(_notifyAllObservers) withObject:nil afterDelay:0 inModes:@[NSRunLoopCommonModes]]; +} + +- (void)_keyboardFrameChanged:(UIView *)keyboard { + if (keyboard != self.keyboardView) return; + + UIWindow *window = keyboard.window; + if (window) { + _observedToFrame = [window convertRect:keyboard.frame toWindow:nil]; + } else { + _observedToFrame = keyboard.frame; + } + _hasObservedChange = YES; + _lastIsNotification = NO; + + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_notifyAllObservers) object:nil]; + [self performSelector:@selector(_notifyAllObservers) withObject:nil afterDelay:0 inModes:@[NSRunLoopCommonModes]]; +} + +- (void)_notifyAllObservers { + UIApplication *app = YYTextSharedApplication(); + if (!app) return; + + UIView *keyboard = self.keyboardView; + UIWindow *window = keyboard.window; + if (!window) { + window = app.keyWindow; + } + if (!window) { + window = app.windows.firstObject; + } + + YYTextKeyboardTransition trans = {0}; + + // from + if (_fromFrame.size.width == 0 && _fromFrame.size.height == 0) { // first notify + _fromFrame.size.width = window.bounds.size.width; + _fromFrame.size.height = trans.toFrame.size.height; + _fromFrame.origin.x = trans.toFrame.origin.x; + _fromFrame.origin.y = window.bounds.size.height; + } + trans.fromFrame = _fromFrame; + trans.fromVisible = _fromVisible; + + // to + if (_lastIsNotification || (_hasObservedChange && CGRectEqualToRect(_observedToFrame, _notificationToFrame))) { + trans.toFrame = _notificationToFrame; + trans.animationDuration = _notificationDuration; + trans.animationCurve = _notificationCurve; + trans.animationOption = _notificationCurve << 16; + + // Fix iPad(iOS7) keyboard frame error after rorate device when the keyboard is not docked to bottom. + if (((int)[self _systemVersion]) == 7) { + UIInterfaceOrientation ori = app.statusBarOrientation; + if (_fromOrientation != UIInterfaceOrientationUnknown && _fromOrientation != ori) { + switch (ori) { + case UIInterfaceOrientationPortrait: { + if (CGRectGetMaxY(trans.toFrame) != window.frame.size.height) { + trans.toFrame.origin.y -= trans.toFrame.size.height; + } + } break; + case UIInterfaceOrientationPortraitUpsideDown: { + if (CGRectGetMinY(trans.toFrame) != 0) { + trans.toFrame.origin.y += trans.toFrame.size.height; + } + } break; + case UIInterfaceOrientationLandscapeLeft: { + if (CGRectGetMaxX(trans.toFrame) != window.frame.size.width) { + trans.toFrame.origin.x -= trans.toFrame.size.width; + } + } break; + case UIInterfaceOrientationLandscapeRight: { + if (CGRectGetMinX(trans.toFrame) != 0) { + trans.toFrame.origin.x += trans.toFrame.size.width; + } + } break; + default: break; + } + } + } + } else { + trans.toFrame = _observedToFrame; + } + + if (window && trans.toFrame.size.width > 0 && trans.toFrame.size.height > 0) { + CGRect rect = CGRectIntersection(window.bounds, trans.toFrame); + if (!CGRectIsNull(rect) && !CGRectIsEmpty(rect)) { + trans.toVisible = YES; + } + } + + if (!CGRectEqualToRect(trans.toFrame, _fromFrame)) { + for (id<YYTextKeyboardObserver> observer in _observers.copy) { + if ([observer respondsToSelector:@selector(keyboardChangedWithTransition:)]) { + [observer keyboardChangedWithTransition:trans]; + } + } + } + + _hasNotification = NO; + _hasObservedChange = NO; + _fromFrame = trans.toFrame; + _fromVisible = trans.toVisible; + _fromOrientation = app.statusBarOrientation; +} + +- (CGRect)convertRect:(CGRect)rect toView:(UIView *)view { + UIApplication *app = YYTextSharedApplication(); + if (!app) return CGRectZero; + + if (CGRectIsNull(rect)) return rect; + if (CGRectIsInfinite(rect)) return rect; + + UIWindow *mainWindow = app.keyWindow; + if (!mainWindow) mainWindow = app.windows.firstObject; + if (!mainWindow) { // no window ?! + if (view) { + [view convertRect:rect fromView:nil]; + } else { + return rect; + } + } + + rect = [mainWindow convertRect:rect fromWindow:nil]; + if (!view) return [mainWindow convertRect:rect toWindow:nil]; + if (view == mainWindow) return rect; + + UIWindow *toWindow = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window; + if (!mainWindow || !toWindow) return [mainWindow convertRect:rect toView:view]; + if (mainWindow == toWindow) return [mainWindow convertRect:rect toView:view]; + + // in different window + rect = [mainWindow convertRect:rect toView:mainWindow]; + rect = [toWindow convertRect:rect fromWindow:mainWindow]; + rect = [view convertRect:rect fromView:toWindow]; + return rect; +} + +@end -- Gitblit v1.8.0