123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 |
- //
- // YYTextEffectWindow.m
- // YYKit <https://github.com/ibireme/YYKit>
- //
- // Created by ibireme on 15/2/25.
- // 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 "YYTextEffectWindow.h"
- #import "YYTextKeyboardManager.h"
- #import "YYKitMacro.h"
- #import "YYCGUtilities.h"
- #import "UIView+YYAdd.h"
- #import "UIApplication+YYAdd.h"
- @implementation YYTextEffectWindow
- + (instancetype)sharedWindow {
- static YYTextEffectWindow *one = nil;
- if (one == nil) {
- // iOS 9 compatible
- NSString *mode = [NSRunLoop currentRunLoop].currentMode;
- if (mode.length == 27 &&
- [mode hasPrefix:@"UI"] &&
- [mode hasSuffix:@"InitializationRunLoopMode"]) {
- return nil;
- }
- }
-
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- if (![UIApplication isAppExtension]) {
- one = [self new];
- one.frame = (CGRect){.size = kScreenSize};
- 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;
- }
- // stop self from becoming the KeyWindow
- - (void)becomeKeyWindow {
- [[[UIApplication sharedExtensionApplication].delegate window] makeKeyWindow];
- }
- - (UIViewController *)rootViewController {
- for (UIWindow *window in [[UIApplication sharedExtensionApplication] windows]) {
- if (self == window) continue;
- if (window.hidden) continue;
- UIViewController *topViewController = window.rootViewController;
- if (topViewController) return topViewController;
- }
- UIViewController *viewController = [super rootViewController];
- if (!viewController) {
- viewController = [UIViewController new];
- [super setRootViewController:viewController];
- }
- return viewController;
- }
- // Bring self to front
- - (void)_updateWindowLevel {
- UIApplication *app = [UIApplication sharedExtensionApplication];
- 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;
- }
- - (YYTextDirection)_keyboardDirection {
- CGRect keyboardFrame = [YYTextKeyboardManager defaultManager].keyboardFrame;
- keyboardFrame = [[YYTextKeyboardManager defaultManager] convertRect:keyboardFrame toView:self];
- if (CGRectIsNull(keyboardFrame) || CGRectIsEmpty(keyboardFrame)) return YYTextDirectionNone;
-
- if (CGRectGetMinY(keyboardFrame) == 0 &&
- CGRectGetMinX(keyboardFrame) == 0 &&
- CGRectGetMaxX(keyboardFrame) == CGRectGetWidth(self.frame))
- return YYTextDirectionTop;
-
- if (CGRectGetMaxX(keyboardFrame) == CGRectGetWidth(self.frame) &&
- CGRectGetMinY(keyboardFrame) == 0 &&
- CGRectGetMaxY(keyboardFrame) == CGRectGetHeight(self.frame))
- return YYTextDirectionRight;
-
- if (CGRectGetMaxY(keyboardFrame) == CGRectGetHeight(self.frame) &&
- CGRectGetMinX(keyboardFrame) == 0 &&
- CGRectGetMaxX(keyboardFrame) == CGRectGetWidth(self.frame))
- return YYTextDirectionBottom;
-
- if (CGRectGetMinX(keyboardFrame) == 0 &&
- CGRectGetMinY(keyboardFrame) == 0 &&
- CGRectGetMaxY(keyboardFrame) == CGRectGetHeight(self.frame))
- return YYTextDirectionLeft;
-
- return YYTextDirectionNone;
- }
- - (CGPoint)_correctedCaptureCenter:(CGPoint)center{
- CGRect keyboardFrame = [YYTextKeyboardManager defaultManager].keyboardFrame;
- keyboardFrame = [[YYTextKeyboardManager defaultManager] convertRect:keyboardFrame toView:self];
- if (!CGRectIsNull(keyboardFrame) && !CGRectIsEmpty(keyboardFrame)) {
- YYTextDirection direction = [self _keyboardDirection];
- switch (direction) {
- case YYTextDirectionTop: {
- if (center.y < CGRectGetMaxY(keyboardFrame)) center.y = CGRectGetMaxY(keyboardFrame);
- } break;
- case YYTextDirectionRight: {
- if (center.x > CGRectGetMinX(keyboardFrame)) center.x = CGRectGetMinX(keyboardFrame);
- } break;
- case YYTextDirectionBottom: {
- if (center.y > CGRectGetMinY(keyboardFrame)) center.y = CGRectGetMinY(keyboardFrame);
- } break;
- case YYTextDirectionLeft: {
- if (center.x < CGRectGetMaxX(keyboardFrame)) center.x = CGRectGetMaxX(keyboardFrame);
- } break;
- default: break;
- }
- }
- return center;
- }
- - (CGPoint)_correctedCenter:(CGPoint)center forMagnifier:(YYTextMagnifier *)mag rotation:(CGFloat)rotation {
- CGFloat degree = RadiansToDegrees(rotation);
-
- degree /= 45.0;
- if (degree < 0) degree += (int)(-degree/8.0 + 1) * 8;
- if (degree > 8) degree -= (int)(degree/8.0) * 8;
-
- CGFloat caretExt = 10;
- if (degree <= 1 || degree >= 7) { //top
- if (mag.type == YYTextMagnifierTypeCaret) {
- if (center.y < caretExt)
- center.y = caretExt;
- } else if (mag.type == YYTextMagnifierTypeRanged) {
- if (center.y < mag.bounds.size.height)
- center.y = mag.bounds.size.height;
- }
- } else if (1 < degree && degree < 3) { // right
- if (mag.type == YYTextMagnifierTypeCaret) {
- if (center.x > self.bounds.size.width - caretExt)
- center.x = self.bounds.size.width - caretExt;
- } else if (mag.type == YYTextMagnifierTypeRanged) {
- if (center.x > self.bounds.size.width - mag.bounds.size.height)
- center.x = self.bounds.size.width - mag.bounds.size.height;
- }
- } else if (3 <= degree && degree <= 5) { // bottom
- if (mag.type == YYTextMagnifierTypeCaret) {
- if (center.y > self.bounds.size.height - caretExt)
- center.y = self.bounds.size.height - caretExt;
- } else if (mag.type == YYTextMagnifierTypeRanged) {
- if (center.y > mag.bounds.size.height)
- center.y = mag.bounds.size.height;
- }
- } else if (5 < degree && degree < 7) { // left
- if (mag.type == YYTextMagnifierTypeCaret) {
- if (center.x < caretExt)
- center.x = caretExt;
- } else if (mag.type == YYTextMagnifierTypeRanged) {
- if (center.x < mag.bounds.size.height)
- center.x = mag.bounds.size.height;
- }
- }
-
- CGRect keyboardFrame = [YYTextKeyboardManager defaultManager].keyboardFrame;
- keyboardFrame = [[YYTextKeyboardManager defaultManager] convertRect:keyboardFrame toView:self];
- if (!CGRectIsNull(keyboardFrame) && !CGRectIsEmpty(keyboardFrame)) {
- YYTextDirection direction = [self _keyboardDirection];
- switch (direction) {
- case YYTextDirectionTop: {
- if (mag.type == YYTextMagnifierTypeCaret) {
- if (center.y - mag.bounds.size.height / 2 < CGRectGetMaxY(keyboardFrame))
- center.y = CGRectGetMaxY(keyboardFrame) + mag.bounds.size.height / 2;
- } else if (mag.type == YYTextMagnifierTypeRanged) {
- if (center.y < CGRectGetMaxY(keyboardFrame)) center.y = CGRectGetMaxY(keyboardFrame);
- }
- } break;
- case YYTextDirectionRight: {
- if (mag.type == YYTextMagnifierTypeCaret) {
- if (center.x + mag.bounds.size.height / 2 > CGRectGetMinX(keyboardFrame))
- center.x = CGRectGetMinX(keyboardFrame) - mag.bounds.size.width / 2;
- } else if (mag.type == YYTextMagnifierTypeRanged) {
- if (center.x > CGRectGetMinX(keyboardFrame)) center.x = CGRectGetMinX(keyboardFrame);
- }
- } break;
- case YYTextDirectionBottom: {
- if (mag.type == YYTextMagnifierTypeCaret) {
- if (center.y + mag.bounds.size.height / 2 > CGRectGetMinY(keyboardFrame))
- center.y = CGRectGetMinY(keyboardFrame) - mag.bounds.size.height / 2;
- } else if (mag.type == YYTextMagnifierTypeRanged) {
- if (center.y > CGRectGetMinY(keyboardFrame)) center.y = CGRectGetMinY(keyboardFrame);
- }
- } break;
- case YYTextDirectionLeft: {
- if (mag.type == YYTextMagnifierTypeCaret) {
- if (center.x - mag.bounds.size.height / 2 < CGRectGetMaxX(keyboardFrame))
- center.x = CGRectGetMaxX(keyboardFrame) + mag.bounds.size.width / 2;
- } else if (mag.type == YYTextMagnifierTypeRanged) {
- if (center.x < CGRectGetMaxX(keyboardFrame)) center.x = CGRectGetMaxX(keyboardFrame);
- }
- } break;
- default: break;
- }
- }
-
- return center;
- }
- /**
- Capture screen snapshot and set it to magnifier.
- @return Magnifier rotation radius.
- */
- - (CGFloat)_updateMagnifier:(YYTextMagnifier *)mag {
- UIApplication *app = [UIApplication sharedExtensionApplication];
- if (!app) return 0;
-
- UIView *hostView = mag.hostView;
- UIWindow *hostWindow = [hostView isKindOfClass:[UIWindow class]] ? (id)hostView : hostView.window;
- if (!hostView || !hostWindow) return 0;
- CGPoint captureCenter = [self convertPoint:mag.hostCaptureCenter fromViewOrWindow:hostView];
- captureCenter = [self _correctedCaptureCenter:captureCenter];
- CGRect captureRect = {.size = mag.snapshotSize};
- captureRect.origin.x = captureCenter.x - captureRect.size.width / 2;
- captureRect.origin.y = captureCenter.y - captureRect.size.height / 2;
-
- CGAffineTransform trans = YYCGAffineTransformGetFromViews(hostView, self);
- CGFloat rotation = CGAffineTransformGetRotation(trans);
-
- if (mag.captureDisabled) {
- if (!mag.snapshot || mag.snapshot.size.width > 1) {
- static UIImage *placeholder;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- CGRect rect = CGRectMake(0, 0, mag.width, mag.height);
- UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
- CGContextRef context = UIGraphicsGetCurrentContext();
- [[UIColor colorWithWhite:1 alpha:0.8] set];
- CGContextFillRect(context, rect);
- placeholder = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
- });
- mag.captureFadeAnimation = YES;
- mag.snapshot = placeholder;
- mag.captureFadeAnimation = NO;
- }
- return rotation;
- }
-
- UIGraphicsBeginImageContextWithOptions(captureRect.size, NO, 0);
- CGContextRef context = UIGraphicsGetCurrentContext();
- if (!context) return rotation;
-
- CGPoint tp = CGPointMake(captureRect.size.width / 2, captureRect.size.height / 2);
- tp = CGPointApplyAffineTransform(tp, CGAffineTransformMakeRotation(rotation));
- CGContextRotateCTM(context, -rotation);
- CGContextTranslateCTM(context, tp.x - captureCenter.x, tp.y - captureCenter.y);
-
- 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;
- else if (w1.windowLevel > w2.windowLevel) return NSOrderedDescending;
- return NSOrderedSame;
- }];
- UIScreen *mainScreen = [UIScreen mainScreen];
- for (UIWindow *window in windows) {
- if (window.hidden || window.alpha <= 0.01) continue;
- if (window.screen != mainScreen) continue;
- if ([window isKindOfClass:self.class]) break; //don't capture window above self
- CGContextSaveGState(context);
- CGContextConcatCTM(context, YYCGAffineTransformGetFromViews(window, self));
- [window.layer renderInContext:context]; //render
- //[window drawViewHierarchyInRect:window.bounds afterScreenUpdates:NO]; //slower when capture whole window
- CGContextRestoreGState(context);
- }
- UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
-
- if (mag.snapshot.size.width == 1) {
- mag.captureFadeAnimation = YES;
- }
- mag.snapshot = image;
- mag.captureFadeAnimation = NO;
- return rotation;
- }
- - (void)showMagnifier:(YYTextMagnifier *)mag {
- if (!mag) return;
- if (mag.superview != self) [self addSubview:mag];
- [self _updateWindowLevel];
- CGFloat rotation = [self _updateMagnifier:mag];
- CGPoint center = [self convertPoint:mag.hostPopoverCenter fromViewOrWindow:mag.hostView];
- CGAffineTransform trans = CGAffineTransformMakeRotation(rotation);
- trans = CGAffineTransformScale(trans, 0.3, 0.3);
- mag.transform = trans;
- mag.center = center;
- if (mag.type == YYTextMagnifierTypeRanged) {
- mag.alpha = 0;
- }
- NSTimeInterval time = mag.type == YYTextMagnifierTypeCaret ? 0.08 : 0.1;
- [UIView animateWithDuration:time delay:0 options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState animations:^{
- if (mag.type == YYTextMagnifierTypeCaret) {
- CGPoint newCenter = CGPointMake(0, -mag.fitSize.height / 2);
- newCenter = CGPointApplyAffineTransform(newCenter, CGAffineTransformMakeRotation(rotation));
- newCenter.x += center.x;
- newCenter.y += center.y;
- mag.center = [self _correctedCenter:newCenter forMagnifier:mag rotation:rotation];
- } else {
- mag.center = [self _correctedCenter:center forMagnifier:mag rotation:rotation];
- }
- mag.transform = CGAffineTransformMakeRotation(rotation);
- mag.alpha = 1;
- } completion:^(BOOL finished) {
-
- }];
- }
- - (void)moveMagnifier:(YYTextMagnifier *)mag {
- if (!mag) return;
- [self _updateWindowLevel];
- CGFloat rotation = [self _updateMagnifier:mag];
- CGPoint center = [self convertPoint:mag.hostPopoverCenter fromViewOrWindow:mag.hostView];
- if (mag.type == YYTextMagnifierTypeCaret) {
- CGPoint newCenter = CGPointMake(0, -mag.fitSize.height / 2);
- newCenter = CGPointApplyAffineTransform(newCenter, CGAffineTransformMakeRotation(rotation));
- newCenter.x += center.x;
- newCenter.y += center.y;
- mag.center = [self _correctedCenter:newCenter forMagnifier:mag rotation:rotation];
- } else {
- mag.center = [self _correctedCenter:center forMagnifier:mag rotation:rotation];
- }
- mag.transform = CGAffineTransformMakeRotation(rotation);
- }
- - (void)hideMagnifier:(YYTextMagnifier *)mag {
- if (!mag) return;
- if (mag.superview != self) return;
- CGFloat rotation = [self _updateMagnifier:mag];
- CGPoint center = [self convertPoint:mag.hostPopoverCenter fromViewOrWindow:mag.hostView];
- NSTimeInterval time = mag.type == YYTextMagnifierTypeCaret ? 0.20 : 0.15;
- [UIView animateWithDuration:time delay:0 options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState animations:^{
-
- CGAffineTransform trans = CGAffineTransformMakeRotation(rotation);
- trans = CGAffineTransformScale(trans, 0.01, 0.01);
- mag.transform = trans;
-
- if (mag.type == YYTextMagnifierTypeCaret) {
- CGPoint newCenter = CGPointMake(0, -mag.fitSize.height / 2);
- newCenter = CGPointApplyAffineTransform(newCenter, CGAffineTransformMakeRotation(rotation));
- newCenter.x += center.x;
- newCenter.y += center.y;
- mag.center = [self _correctedCenter:newCenter forMagnifier:mag rotation:rotation];
- } else {
- mag.center = [self _correctedCenter:center forMagnifier:mag rotation:rotation];
- mag.alpha = 0;
- }
-
- } completion:^(BOOL finished) {
- if (finished) {
- [mag removeFromSuperview];
- mag.transform = CGAffineTransformIdentity;
- mag.alpha = 1;
- }
- }];
- }
- - (void)_updateSelectionGrabberDot:(YYSelectionGrabberDot *)dot selection:(YYTextSelectionView *)selection{
- dot.mirror.hidden = YES;
- if (selection.hostView.clipsToBounds == YES && dot.visibleAlpha > 0.1) {
- CGRect dotRect = [dot convertRect:dot.bounds toViewOrWindow:self];
- BOOL dotInKeyboard = NO;
-
- CGRect keyboardFrame = [YYTextKeyboardManager defaultManager].keyboardFrame;
- keyboardFrame = [[YYTextKeyboardManager defaultManager] convertRect:keyboardFrame toView:self];
- if (!CGRectIsNull(keyboardFrame) && !CGRectIsEmpty(keyboardFrame)) {
- CGRect inter = CGRectIntersection(dotRect, keyboardFrame);
- if (!CGRectIsNull(inter) && (inter.size.width > 1 || inter.size.height > 1)) {
- dotInKeyboard = YES;
- }
- }
- if (!dotInKeyboard) {
- CGRect hostRect = [selection.hostView convertRect:selection.hostView.bounds toView:self];
- CGRect intersection = CGRectIntersection(dotRect, hostRect);
- if (CGRectGetArea(intersection) < CGRectGetArea(dotRect)) {
- CGFloat dist = CGPointGetDistanceToRect(CGRectGetCenter(dotRect), hostRect);
- if (dist < CGRectGetWidth(dot.frame) * 0.55) {
- dot.mirror.hidden = NO;
- }
- }
- }
- }
- CGPoint center = [dot convertPoint:CGPointMake(CGRectGetWidth(dot.frame) / 2, CGRectGetHeight(dot.frame) / 2) toViewOrWindow:self];
- if (isnan(center.x) || isnan(center.y) || isinf(center.x) || isinf(center.y)) {
- dot.mirror.hidden = YES;
- } else {
- dot.mirror.center = center;
- }
- }
- - (void)showSelectionDot:(YYTextSelectionView *)selection {
- if (!selection) return;
- [self _updateWindowLevel];
- [self insertSubview:selection.startGrabber.dot.mirror atIndex:0];
- [self insertSubview:selection.endGrabber.dot.mirror atIndex:0];
- [self _updateSelectionGrabberDot:selection.startGrabber.dot selection:selection];
- [self _updateSelectionGrabberDot:selection.endGrabber.dot selection:selection];
- }
- - (void)hideSelectionDot:(YYTextSelectionView *)selection {
- if (!selection) return;
- [selection.startGrabber.dot.mirror removeFromSuperview];
- [selection.endGrabber.dot.mirror removeFromSuperview];
- }
- @end
|