123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- //
- // BubbleView.m
- // XenonSDK
- //
- // Created by SAGESSE on 2019/5/30.
- // Copyright © 2019 SAGESSE. All rights reserved.
- //
- #import "BubbleView.h"
- #import "XenonSDK.h"
- @interface BubbleView () <UIGestureRecognizerDelegate>
- @property (nonatomic, strong) NSTimer* timer;
- @property (nonatomic, strong) UIPanGestureRecognizer* panGestureRecognizer;
- @end
- @implementation BubbleView
- - (instancetype)initWithFrame:(CGRect)frame {
- self = [super initWithFrame:frame];
-
- self.contentView = [UIButton buttonWithType:UIButtonTypeSystem];
-
- UIImage* image = [UIImage imageNamed:@"login_mine" inBundle:XenonSDK.sharedSDK.bundle compatibleWithTraitCollection:nil];
-
-
- self.contentView.frame = self.bounds;
- self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- [self.contentView setImage:[image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]
- forState:UIControlStateNormal];
- [self addSubview:self.contentView];
-
- return self;
- }
- - (void)startCountdonw {
- // Setting timer.
- NSTimer* timer = [NSTimer timerWithTimeInterval:3 target:self selector:@selector(echo:) userInfo:nil repeats:false];
- [NSRunLoop.mainRunLoop addTimer:timer forMode:NSDefaultRunLoopMode];
- self.timer = timer;
- }
- - (void)stopCountdown {
- if (self.timer == nil) {
- return;
- }
-
- [self.timer invalidate];
- self.timer = nil;
-
- // Restore cursor state.
- self.transform = CGAffineTransformIdentity;
- self.contentView.hidden = NO;
- self.gradualView.hidden = YES;
- }
- - (CGPoint)estimateRange:(CGPoint)center inView:(UIView*)view {
- if (view == nil) {
- return center;
- }
-
- CGRect bounds = UIEdgeInsetsInsetRect(view.bounds, UIEdgeInsetsMake(8, 8, 8, 8));;
-
- CGFloat x = center.x;
- CGFloat y = center.y;
-
- // Check that the distance side is closer.
- if (fabs((x - self.frame.size.width / 2) - CGRectGetMinX(bounds)) < fabs((x + self.frame.size.width / 2) - CGRectGetMaxX(bounds))) {
- x = CGRectGetMinX(bounds) + self.frame.size.width / 2;
- } else {
- x = CGRectGetMaxX(bounds) - self.frame.size.width / 2;
- }
-
- // You can't go above or below the boundary.
- y = fmin(fmax(y, CGRectGetMinY(bounds) + self.frame.size.height / 2), CGRectGetMaxY(bounds) - self.frame.size.height / 2);
- return CGPointMake(x, y);
- }
- - (void)echo:(NSTimer*)timer {
- // Gets the window.
- if (self.window == nil) {
- return;
- }
-
- // Only need to create the view once.
- if (self.gradualView == nil) {
-
- // If no image is provided, ignore it.
- UIImage* image = [UIImage imageNamed:@"login_iv_hide" inBundle:XenonSDK.sharedSDK.bundle compatibleWithTraitCollection:nil];
- if (image == nil) {
- return;
- }
- // Calculate the best display effect.
- CGFloat height = self.bounds.size.height;
- CGFloat width = height * (image.size.width / image.size.height);
-
- // Create the view and set the same default values.
-
- UIImageView* imageView = [[UIImageView alloc] initWithImage:image];
-
- imageView.bounds = CGRectMake(0, 0, width, height);
-
- imageView.hidden = NO;
- imageView.userInteractionEnabled = NO;
- imageView.contentMode = UIViewContentModeScaleAspectFit;
-
- [self addSubview:imageView];
- self.gradualView = imageView;
- }
- // Click is not allowed while hidden.
- self.contentView.hidden = false;
- self.gradualView.hidden = true;
- // Calculate the distance to the boundary.
- CGFloat offset = 0;
- if (fabs(CGRectGetMinX(self.frame)) < fabs(self.window.frame.size.width - CGRectGetMaxX(self.frame))) {
- // In left side.
- offset = -CGRectGetMaxX(self.frame);
- self.gradualView.transform = CGAffineTransformIdentity;
- self.gradualView.frame = CGRectMake(-CGRectGetMinX(self.frame), 0, self.gradualView.frame.size.width, self.gradualView.frame.size.height);
-
- } else {
- // In right side.
- offset = self.window.frame.size.width - CGRectGetMinX(self.frame);
- self.gradualView.transform = CGAffineTransformMakeScale(-1, 1);
- self.gradualView.frame = CGRectMake(offset - self.gradualView.frame.size.width, 0, self.gradualView.frame.size.width, self.gradualView.frame.size.height);
- }
- // Generate hidden animation.
- [UIView animateWithDuration:0.2 animations:^{
-
- // step 1: hide the entire button.
- self.transform = CGAffineTransformMakeTranslation(offset, 0);
- } completion:^(BOOL finished) {
-
- // step 2: Disable click events to show hidden cursors.
- self.contentView.hidden = true;
- self.gradualView.hidden = false;
-
- // step 3: Generate display animation.
- [UIView animateWithDuration:0.15 animations:^{
- self.transform = CGAffineTransformIdentity;
- }];
- }];
-
- [timer invalidate];
- }
- - (void)move:(UIPanGestureRecognizer*)sender {
-
- CGPoint location = [sender translationInView:self.superview];
- switch (sender.state) {
- case UIGestureRecognizerStateBegan:
- case UIGestureRecognizerStateChanged:
- case UIGestureRecognizerStatePossible: {
- // Begin drag event.
- self.transform = CGAffineTransformMakeTranslation(location.x, location.y);
- [self stopCountdown];
- break;
- }
- case UIGestureRecognizerStateEnded: {
- // End drag event.
- self.transform = CGAffineTransformIdentity;
- self.center = CGPointMake(self.center.x + location.x, self.center.y + location.y);
- [self startCountdonw];
-
- // Generate move animation.
- [UIView animateWithDuration:0.25 animations:^{
- self.center = [self estimateRange:self.center inView:self.superview];
- }];
- // Getting the class name directly is to reduce the number of strings in the code.
- [XSDataCenter setValue:@[@(self.center.x), @(self.center.y)] forKey:NSStringFromClass(self.class)];
- break;
- }
- default: {
- // End drag event in cancel.
- self.transform = CGAffineTransformIdentity;
- [self startCountdonw];
- break;
- }
- }
- }
- - (void)setHidden:(BOOL)hidden {
- [super setHidden:hidden];
-
- if (self.timer == nil || !self.gradualView.hidden) {
- return;
- }
-
- // If you are hiding, countdown is not allowed.
- if (hidden) {
- // Cancel countdown.
- [self.timer invalidate];
- } else {
- // Restore countdown.
- [self startCountdonw];
- }
-
- }
- - (void)setCenter:(CGPoint)center {
- [super setCenter:center];
-
- if (self.superview == nil) {
- return;
- }
-
- if (CGRectGetMinX(self.frame) < CGRectGetWidth(self.superview.bounds) / 2) {
- // In left side.
- self.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
- } else {
- // In right side.
- self.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
- }
- }
- - (void)willMoveToSuperview:(UIView *)newSuperview {
-
- // Remove or Add?
- if (newSuperview == nil) {
- // When superview remove, the pan gesture recognizer need remove.
- [self.panGestureRecognizer.view removeGestureRecognizer:self.panGestureRecognizer];
- self.panGestureRecognizer = nil;
- [self stopCountdown];
- return;
- }
- if (self.panGestureRecognizer != nil) {
- return ;
- }
-
- CGPoint center = CGPointMake(newSuperview.bounds.size.width, newSuperview.bounds.size.height / 2);
- // Getting the class name directly is to reduce the number of strings in the code.
- NSArray* data = [XSDataCenter valueForKey:NSStringFromClass(self.class)];
- if (data.count == 2) {
- center.x = [data[0] doubleValue];
- center.y = [data[1] doubleValue];
- }
-
- UIPanGestureRecognizer* panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(move:)];
-
- panGestureRecognizer.delegate = self;
- panGestureRecognizer.delaysTouchesBegan = false;
- panGestureRecognizer.delaysTouchesEnded = true;
- panGestureRecognizer.cancelsTouchesInView = true;
- [newSuperview addGestureRecognizer:panGestureRecognizer];
- self.center = [self estimateRange:center inView:newSuperview];
- self.panGestureRecognizer = panGestureRecognizer;
- [self startCountdonw];
- }
- - (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
-
- if (self.hidden || !self.userInteractionEnabled) {
- return nil;
- }
-
- if (CGRectContainsPoint(self.contentView.frame, point) && !self.contentView.hidden) {
- return self.contentView;
- }
-
- if (self.gradualView != nil && CGRectContainsPoint(self.gradualView.frame, point) && !self.gradualView.hidden) {
- return self.gradualView;
- }
- return nil;
- }
- - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
- if (self.hidden) {
- return NO;
- }
- CGRect bounds = UIEdgeInsetsInsetRect(self.bounds, UIEdgeInsetsMake(-20, -20, -20, -20));
- return CGRectContainsPoint(bounds, [gestureRecognizer locationInView:self]);
- }
- @end
|