Browse Source

check App Extension in runtime to support App Extension target

ibireme 9 years ago
parent
commit
8416977ce4

+ 6 - 4
README.md

@@ -499,8 +499,9 @@ Installation
     * MobileCoreServices
 4. Import `YYText.h`.
 
-*  If you want to support animated image (GIF/APNG/WebP), you may add [YYImage](https://github.com/ibireme/YYWebImage) or [YYWebImage](https://github.com/ibireme/YYWebImage) to your project.
-*  If you want to use it in App Extension, you may add `YY_TARGET_IS_EXTENSION` flag in `Build Settings` - `Preprocessor Macros` sections.
+
+### Notice
+You may add [YYImage](https://github.com/ibireme/YYWebImage) or [YYWebImage](https://github.com/ibireme/YYWebImage) to your project if you want to support animated image (GIF/APNG/WebP).
 
 
 Documentation
@@ -1020,8 +1021,9 @@ YYText 和 TextKit 架构对比
     * MobileCoreServices
 4. 导入 `YYText.h`。
 
-*  如果需要支持动图(GIF/APNG/WebP),你可以添加 [YYImage](https://github.com/ibireme/YYWebImage) 或 [YYWebImage](https://github.com/ibireme/YYWebImage) 到你的工程里。
-*  如果你需要在 App Extension 中使用这些代码, 可以在 `Build Settings` - `Preprocessor Macros` 中添加 `YY_TARGET_IS_EXTENSION` 参数。
+
+### 注意
+你可以添加 [YYImage](https://github.com/ibireme/YYWebImage) 或 [YYWebImage](https://github.com/ibireme/YYWebImage) 到你的工程,以支持动画格式(GIF/APNG/WebP)的图片。
 
 
 文档

+ 1 - 1
YYText/Component/YYTextEffectWindow.h

@@ -27,7 +27,7 @@
  */
 @interface YYTextEffectWindow : UIWindow
 
-/// Returns the shared instance.
+/// Returns the shared instance (returns nil in App Extension).
 + (instancetype)sharedWindow;
 
 /// Show the magnifier in this window with a 'popup' animation. @param mag A magnifier.

+ 23 - 23
YYText/Component/YYTextEffectWindow.m

@@ -18,34 +18,35 @@
 @implementation YYTextEffectWindow
 
 + (instancetype)sharedWindow {
-    static YYTextEffectWindow *one;
+    static YYTextEffectWindow *one = nil;
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
-#ifndef YY_TARGET_IS_EXTENSION
-        one = [self new];
-        one.frame = (CGRect){.size = YYTextScreenSize()};
-        one.userInteractionEnabled = NO;
-        one.windowLevel = UIWindowLevelStatusBar + 1;
-        one.hidden = NO;
-        
-        // for iOS 9:
-        one.opaque = NO;
-        one.backgroundColor = [UIColor clearColor];
-        one.layer.backgroundColor = [UIColor clearColor].CGColor;
-#endif
+        if (!YYTextIsAppExtension()) {
+            one = [self new];
+            one.frame = (CGRect){.size = YYTextScreenSize()};
+            one.userInteractionEnabled = NO;
+            one.windowLevel = UIWindowLevelStatusBar + 1;
+            one.hidden = NO;
+            
+            // for iOS 9:
+            one.opaque = NO;
+            one.backgroundColor = [UIColor clearColor];
+            one.layer.backgroundColor = [UIColor clearColor].CGColor;
+        }
     });
     return one;
 }
 
 // Bring self to front
 - (void)_updateWindowLevel {
-#ifndef YY_TARGET_IS_EXTENSION
-    UIWindow *top = [UIApplication sharedApplication].windows.lastObject;
-    UIWindow *key = [UIApplication sharedApplication].keyWindow;
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return;
+    
+    UIWindow *top = app.windows.lastObject;
+    UIWindow *key = app.keyWindow;
     if (key && key.windowLevel > top.windowLevel) top = key;
     if (top == self) return;
     self.windowLevel = top.windowLevel + 1;
-#endif
 }
 
 - (YYTextDirection)_keyboardDirection {
@@ -192,7 +193,9 @@
  @return Magnifier rotation radius.
  */
 - (CGFloat)_updateMagnifier:(YYTextMagnifier *)mag {
-#ifndef YY_TARGET_IS_EXTENSION
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return 0;
+    
     UIView *hostView = mag.hostView;
     UIWindow *hostWindow = [hostView isKindOfClass:[UIWindow class]] ? (id)hostView : hostView.window;
     if (!hostView || !hostWindow) return 0;
@@ -235,8 +238,8 @@
     CGContextRotateCTM(context, -rotation);
     CGContextTranslateCTM(context, tp.x - captureCenter.x, tp.y - captureCenter.y);
     
-    NSMutableArray *windows = [UIApplication sharedApplication].windows.mutableCopy;
-    UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
+    NSMutableArray *windows = app.windows.mutableCopy;
+    UIWindow *keyWindow = app.keyWindow;
     if (![windows containsObject:keyWindow]) [windows addObject:keyWindow];
     [windows sortUsingComparator:^NSComparisonResult(UIWindow *w1, UIWindow *w2) {
         if (w1.windowLevel < w2.windowLevel) return NSOrderedAscending;
@@ -263,9 +266,6 @@
     mag.snapshot = image;
     mag.captureFadeAnimation = NO;
     return rotation;
-#else
-    return 0;
-#endif
 }
 
 - (void)showMagnifier:(YYTextMagnifier *)mag {

+ 1 - 1
YYText/Component/YYTextKeyboardManager.h

@@ -48,7 +48,7 @@ typedef struct {
 - (instancetype)init UNAVAILABLE_ATTRIBUTE;
 + (instancetype)new UNAVAILABLE_ATTRIBUTE;
 
-/// Get the default manager.
+/// Get the default manager (returns nil in App Extension).
 + (instancetype)defaultManager;
 
 /// Get the keyboard window. nil if there's no keyboard window.

+ 28 - 25
YYText/Component/YYTextKeyboardManager.m

@@ -10,6 +10,7 @@
 //
 
 #import "YYTextKeyboardManager.h"
+#import "YYTextUtilities.h"
 #import <objc/runtime.h>
 
 
@@ -137,20 +138,20 @@ static int _YYTextKeyboardViewFrameObserverKey;
 }
 
 + (instancetype)defaultManager {
-    static YYTextKeyboardManager *mgr;
+    static YYTextKeyboardManager *mgr = nil;
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
-        mgr = [[self alloc] _init];
+        if (!YYTextIsAppExtension()) {
+            mgr = [[self alloc] _init];
+        }
     });
     return mgr;
 }
 
 + (void)load {
-#ifndef YY_TARGET_IS_EXTENSION
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
         [self defaultManager];
     });
-#endif
 }
 
 - (void)addObserver:(id<YYTextKeyboardObserver>)observer {
@@ -164,16 +165,18 @@ static int _YYTextKeyboardViewFrameObserverKey;
 }
 
 - (UIWindow *)keyboardWindow {
-#ifndef YY_TARGET_IS_EXTENSION
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return nil;
+    
     UIWindow *window = nil;
-    for (window in [UIApplication sharedApplication].windows) {
+    for (window in app.windows) {
         if ([self _getKeyboardViewFromWindow:window]) return window;
     }
-    window = [UIApplication sharedApplication].keyWindow;
+    window = app.keyWindow;
     if ([self _getKeyboardViewFromWindow:window]) return window;
     
     NSMutableArray *kbWindows = nil;
-    for (window in [UIApplication sharedApplication].windows) {
+    for (window in app.windows) {
         NSString *windowName = NSStringFromClass(window.class);
         if ([self _systemVersion] < 9) {
             // UITextEffectsWindow
@@ -197,22 +200,22 @@ static int _YYTextKeyboardViewFrameObserverKey;
     if (kbWindows.count == 1) {
         return kbWindows.firstObject;
     }
-#endif
     return nil;
 }
 
 - (UIView *)keyboardView {
-#ifndef YY_TARGET_IS_EXTENSION
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return nil;
+    
     UIWindow *window = nil;
     UIView *view = nil;
-    for (window in [UIApplication sharedApplication].windows) {
+    for (window in app.windows) {
         view = [self _getKeyboardViewFromWindow:window];
         if (view) return view;
     }
-    window = [UIApplication sharedApplication].keyWindow;
+    window = app.keyWindow;
     view = [self _getKeyboardViewFromWindow:window];
     if (view) return view;
-#endif
     return nil;
 }
 
@@ -367,14 +370,16 @@ static int _YYTextKeyboardViewFrameObserverKey;
 }
 
 - (void)_notifyAllObservers {
-#ifndef YY_TARGET_IS_EXTENSION
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return;
+    
     UIView *keyboard = self.keyboardView;
     UIWindow *window = keyboard.window;
     if (!window) {
-        window = [UIApplication sharedApplication].keyWindow;
+        window = app.keyWindow;
     }
     if (!window) {
-        window = [UIApplication sharedApplication].windows.firstObject;
+        window = app.windows.firstObject;
     }
     
     YYTextKeyboardTransition trans = {0};
@@ -398,7 +403,7 @@ static int _YYTextKeyboardViewFrameObserverKey;
         
         // Fix iPad(iOS7) keyboard frame error after rorate device when the keyboard is not docked to bottom.
         if (((int)[self _systemVersion]) == 7) {
-            UIInterfaceOrientation ori = [UIApplication sharedApplication].statusBarOrientation;
+            UIInterfaceOrientation ori = app.statusBarOrientation;
             if (_fromOrientation != UIInterfaceOrientationUnknown && _fromOrientation != ori) {
                 switch (ori) {
                     case UIInterfaceOrientationPortrait: {
@@ -448,17 +453,18 @@ static int _YYTextKeyboardViewFrameObserverKey;
     _hasObservedChange = NO;
     _fromFrame = trans.toFrame;
     _fromVisible = trans.toVisible;
-    _fromOrientation = [UIApplication sharedApplication].statusBarOrientation;
-#endif
+    _fromOrientation = app.statusBarOrientation;
 }
 
 - (CGRect)convertRect:(CGRect)rect toView:(UIView *)view {
-#ifndef YY_TARGET_IS_EXTENSION
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return CGRectZero;
+    
     if (CGRectIsNull(rect)) return rect;
     if (CGRectIsInfinite(rect)) return rect;
     
-    UIWindow *mainWindow = [UIApplication sharedApplication].keyWindow;
-    if (!mainWindow) mainWindow = [UIApplication sharedApplication].windows.firstObject;
+    UIWindow *mainWindow = app.keyWindow;
+    if (!mainWindow) mainWindow = app.windows.firstObject;
     if (!mainWindow) { // no window ?!
         if (view) {
             [view convertRect:rect fromView:nil];
@@ -480,9 +486,6 @@ static int _YYTextKeyboardViewFrameObserverKey;
     rect = [toWindow convertRect:rect fromWindow:mainWindow];
     rect = [view convertRect:rect fromView:toWindow];
     return rect;
-#else
-    return CGRectZero;
-#endif
 }
 
 @end

+ 7 - 0
YYText/Utility/YYTextUtilities.h

@@ -551,3 +551,10 @@ static inline NSRange YYNSRangeFromCFRange(CFRange range) {
 static inline CFRange YYCFRangeFromNSRange(NSRange range) {
     return CFRangeMake(range.location, range.length);
 }
+
+
+/// Returns YES in App Extension.
+BOOL YYTextIsAppExtension();
+
+/// Returns nil in App Extension.
+UIApplication *YYTextSharedApplication();

+ 19 - 0
YYText/Utility/YYTextUtilities.m

@@ -281,3 +281,22 @@ CGSize YYTextScreenSize() {
     });
     return size;
 }
+
+
+BOOL YYTextIsAppExtension() {
+    static BOOL isAppExtension = NO;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        Class cls = NSClassFromString(@"UIApplication");
+        if(!cls || ![cls respondsToSelector:@selector(sharedApplication)]) isAppExtension = YES;
+        if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]) isAppExtension = YES;
+    });
+    return isAppExtension;
+}
+
+UIApplication *YYTextSharedApplication() {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundeclared-selector"
+    return YYTextIsAppExtension() ? nil : [UIApplication performSelector:@selector(sharedApplication)];
+#pragma clang diagnostic pop
+}

+ 21 - 8
YYText/YYTextView.m

@@ -453,6 +453,8 @@ typedef NS_ENUM(NSUInteger, YYTextMoveDirection) {
 
 /// Show or update `_magnifierCaret` based on `_trackingPoint`, and hide `_magnifierRange`.
 - (void)_showMagnifierCaret {
+    if (YYTextIsAppExtension()) return;
+    
     if (_state.showingMagnifierRanged) {
         _state.showingMagnifierRanged = NO;
         [[YYTextEffectWindow sharedWindow] hideMagnifier:_magnifierRanged];
@@ -470,6 +472,8 @@ typedef NS_ENUM(NSUInteger, YYTextMoveDirection) {
 
 /// Show or update `_magnifierRanged` based on `_trackingPoint`, and hide `_magnifierCaret`.
 - (void)_showMagnifierRanged {
+    if (YYTextIsAppExtension()) return;
+    
     if (_verticalForm) { // hack for vertical form...
         [self _showMagnifierCaret];
         return;
@@ -542,6 +546,8 @@ typedef NS_ENUM(NSUInteger, YYTextMoveDirection) {
 
 /// Update the showing magnifier.
 - (void)_updateMagnifier {
+    if (YYTextIsAppExtension()) return;
+    
     if (_state.showingMagnifierCaret) {
         [[YYTextEffectWindow sharedWindow] moveMagnifier:_magnifierCaret];
     }
@@ -552,6 +558,8 @@ typedef NS_ENUM(NSUInteger, YYTextMoveDirection) {
 
 /// Hide the `_magnifierCaret` and `_magnifierRanged`.
 - (void)_hideMagnifier {
+    if (YYTextIsAppExtension()) return;
+    
     if (_state.showingMagnifierCaret || _state.showingMagnifierRanged) {
         // disable touch began temporary to ignore caret animation overlap
         _state.ignoreTouchBegan = YES;
@@ -1040,6 +1048,7 @@ typedef NS_ENUM(NSUInteger, YYTextMoveDirection) {
 /// If it shows selection grabber and this view was moved by super view,
 /// update the selection dot in window.
 - (void)_fixSelectionDot {
+    if (YYTextIsAppExtension()) return;
     CGPoint origin = [self yy_convertPoint:CGPointZero toViewOrWindow:[YYTextEffectWindow sharedWindow]];
     if (!CGPointEqualToPoint(origin, _previousOriginInWindow)) {
         _previousOriginInWindow = origin;
@@ -1563,10 +1572,9 @@ typedef NS_ENUM(NSUInteger, YYTextMoveDirection) {
 /// Returns the `root` view controller (returns nil if not found).
 - (UIViewController *)_getRootViewController {
     UIViewController *ctrl = nil;
-#ifndef YY_TARGET_IS_EXTENSION
-    if (!ctrl) ctrl = [UIApplication sharedApplication].keyWindow.rootViewController;
-    if (!ctrl) ctrl = [[UIApplication sharedApplication].windows.firstObject rootViewController];
-#endif
+    UIApplication *app = YYTextSharedApplication();
+    if (!ctrl) ctrl = app.keyWindow.rootViewController;
+    if (!ctrl) ctrl = [app.windows.firstObject rootViewController];
     if (!ctrl) ctrl = self.yy_viewController;
     if (!ctrl) return nil;
     
@@ -1663,8 +1671,8 @@ typedef NS_ENUM(NSUInteger, YYTextMoveDirection) {
 }
 
 /// Show undo alert if it can undo or redo.
-- (void)_showUndoAlert {
-#ifndef YY_TARGET_IS_EXTENSION
+#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
+- (void)_showUndoRedoAlert NS_EXTENSION_UNAVAILABLE_IOS(""){
     _state.firstResponderBeforeUndoAlert = self.isFirstResponder;
     __weak typeof(self) _self = self;
     NSArray *strings = [self _localizedUndoStrings];
@@ -1732,8 +1740,8 @@ typedef NS_ENUM(NSUInteger, YYTextMoveDirection) {
 #pragma clang diagnostic pop
         }
     }
-#endif
 }
+#endif
 
 /// Get the localized undo alert strings based on app's main bundle.
 - (NSArray *)_localizedUndoStrings {
@@ -2715,7 +2723,12 @@ typedef NS_ENUM(NSUInteger, YYTextMoveDirection) {
 
 - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
     if (motion == UIEventSubtypeMotionShake && _allowsUndoAndRedo) {
-        [self _showUndoAlert];
+        if (!YYTextIsAppExtension()) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundeclared-selector"
+            [self performSelector:@selector(_showUndoRedoAlert)];
+#pragma clang diagnostic pop
+        }
     } else {
         [super motionEnded:motion withEvent:event];
     }