2021-11-30

//

//  HWPopController.m

//  HWPopController

//

//  Created by heath wang on 2019/5/21.

//

#import "HWPopController.h"

#import "UIViewController+HWPopController.h"

#import "HWPopTransitioningDelegate.h"

staticNSMutableSet*_retainedPopControllers;

@interface UIViewController (Internal)

@property (nonatomic, weak) HWPopController *popController;

@end

@interface HWPopContainerViewController : UIViewController

@end

@implementation HWPopContainerViewController

@end

@interface HWPopController ()

@property (nonatomic, strong) HWPopContainerViewController *containerViewController;

@property (nonatomic, strong) UIViewController *topViewController;

@property (nonatomic, strong) UIView *containerView;

@property (nonatomic, strong) UIView *contentView;

@property (nonatomic, assign) BOOL didOverrideSafeAreaInsets;

@property (nonatomic, assign) BOOL isObserving;

@property (nonatomic, copy) NSDictionary *keyboardInfo;

@property (nonatomic, strong) HWPopTransitioningDelegate *transitioningDelegate;

@end

@implementation HWPopController

#pragma mark- init

+ (void)load{

    staticdispatch_once_tonceToken;

    dispatch_once(&onceToken, ^{

        _retainedPopControllers = [NSMutableSet set];

    });

}

- (instancetype)init {

    self= [superinit];

    if(self) {

        [selfsetup];

    }

    return self;

}

#pragma mark - public method

- (instancetype)initWithViewController:(UIViewController*)viewController {

    self= [selfinit];

    if(self) {

        self.topViewController= viewController;

        // set popController to the popped viewController

        viewController.popController=self;

        [self setupObserverForViewController:viewController];

    }

    return self;

}

- (void)presentInViewController:(UIViewController*)presentingViewController {

    [selfpresentInViewController:presentingViewControllercompletion:nil];

}

- (void)presentInViewController:(UIViewController*)presentingViewControllercompletion:(nullablevoid(^)(void))completion {

    if (self.presented)

        return;



    dispatch_async(dispatch_get_main_queue(), ^{

        [self setupObserver];

        [_retainedPopControllers addObject:self];


        UIViewController*VC = presentingViewController.tabBarController?: presentingViewController;


        if(@available(iOS11.0, *)) {

            if (!self.didOverrideSafeAreaInsets) {

                self.safeAreaInsets= presentingViewController.view.safeAreaInsets;

            }

        }


        [VCpresentViewController:self.containerViewController animated:YES completion:completion];

    });

}

- (void)dismiss {

    [self dismissWithCompletion:nil];

}

- (void)dismissWithCompletion:(nullablevoid(^)(void))completion {

    if(!self.presented)

        return;


    dispatch_async(dispatch_get_main_queue(), ^{

        [self destroyObserver];

        [self.containerViewController dismissViewControllerAnimated:YES completion:^{

            [_retainedPopControllers removeObject:self];

            completion ? completion() :nil;

        }];

    });

}

#pragma mark- observe

- (void)setupObserverForViewController:(UIViewController *)viewController {

    [viewControlleraddObserver:self forKeyPath:NSStringFromSelector(@selector(contentSizeInPop)) options:NSKeyValueObservingOptionNew context:nil];

    [viewControlleraddObserver:self forKeyPath:NSStringFromSelector(@selector(contentSizeInPopWhenLandscape)) options:NSKeyValueObservingOptionNew context:nil];

}

- (void)setupObserver {

    if (self.isObserving)

        return;

    // Observe orientation change

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];

    // Observe keyboard

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillChangeFrameNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];

    self.isObserving = YES;

}

- (void)destroyObserver {

    if (!self.isObserving)

        return;

    [[NSNotificationCenter defaultCenter] removeObserver:self];

    self.isObserving = NO;

}

- (void)destroyObserverOfViewController:(UIViewController *)viewController {

    [viewControllerremoveObserver:selfforKeyPath:NSStringFromSelector(@selector(contentSizeInPop))];

    [viewControllerremoveObserver:selfforKeyPath:NSStringFromSelector(@selector(contentSizeInPopWhenLandscape))];

}

- (void)observeValueForKeyPath:(nullableNSString*)keyPathofObject:(nullableid)objectchange:(nullableNSDictionary *)changecontext:(nullablevoid*)context {

    if(object ==self.topViewController) {

        if (self.topViewController.isViewLoaded && self.topViewController.view.superview) {

            [UIView animateWithDuration:0.35 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseIn animations:^{

                [selflayoutContainerView];

            }completion:^(BOOLfinished) {

                [self adjustContainerViewOrigin];

            }];

        }

    }

}

#pragma mark - UIApplicationDidChangeStatusBarOrientationNotification

- (void)orientationDidChange {

    [self.containerView endEditing:YES];

    [UIView animateWithDuration:0.25 animations:^{

        [self layoutContainerView];

    }completion:^(BOOLfinished) {

    }];

}

#pragma mark - keyboard handle

- (void)adjustContainerViewOrigin {

    if (!self.keyboardInfo)

        return;

    UIView <UIKeyInput> *currentTextInput = [self getCurrentTextInputInView:self.containerView];

    if(!currentTextInput) {

        return;

    }

    CGAffineTransform lastTransform = self.containerView.transform;

    self.containerView.transform = CGAffineTransformIdentity;

    CGFloattextFieldBottomY = [currentTextInputconvertPoint:CGPointZerotoView:self.containerViewController.view].y+ currentTextInput.bounds.size.height;

    CGFloat keyboardHeight = [self.keyboardInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;

    // For iOS 7

    UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1 &&

            (orientation ==UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight)) {

        keyboardHeight = [self.keyboardInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.width;

    }

    CGFloatoffsetY =0;

    if (self.popPosition == HWPopPositionBottom) {

        offsetY = keyboardHeight -_safeAreaInsets.bottom;

    }else{

        CGFloat statusBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height;

        if(self.containerView.bounds.size.height<=self.containerViewController.view.bounds.size.height- keyboardHeight - statusBarHeight) {

            offsetY =self.containerView.frame.origin.y- (statusBarHeight + (self.containerViewController.view.bounds.size.height- keyboardHeight - statusBarHeight -self.containerView.bounds.size.height) /2);

        }else{

            CGFloatspacing =5;

            offsetY =self.containerView.frame.origin.y+self.containerView.bounds.size.height- (self.containerViewController.view.bounds.size.height- keyboardHeight - spacing);

            if (offsetY <= 0) { // self.containerView can be totally shown, so no need to translate the origin

                return;

            }

            if (self.containerView.frame.origin.y - offsetY < statusBarHeight) { // self.containerView will be covered by status bar if the origin is translated by "offsetY"

                offsetY =self.containerView.frame.origin.y- statusBarHeight;

                // currentTextField can not be totally shown if self.containerView is going to repositioned with "offsetY"

                if(textFieldBottomY - offsetY >self.containerViewController.view.bounds.size.height- keyboardHeight - spacing) {

                    offsetY = textFieldBottomY - (self.containerViewController.view.bounds.size.height- keyboardHeight - spacing);

                }

            }

        }

    }

    NSTimeInterval duration = [self.keyboardInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];

    UIViewAnimationCurve curve = [self.keyboardInfo[UIKeyboardAnimationCurveUserInfoKey] intValue];

    self.containerView.transform= lastTransform;// Restore transform

    [UIView beginAnimations:nil context:NULL];

    [UIView setAnimationBeginsFromCurrentState:YES];

    [UIView setAnimationCurve:curve];

    [UIView setAnimationDuration:duration];

    self.containerView.transform = CGAffineTransformMakeTranslation(0, -offsetY);

    [UIView commitAnimations];

}

- (void)keyboardWillShow:(NSNotification*)notification {

//    UIView *currentTextInput = [self getCurrentTextInputInView:self.containerView];

//    if (!currentTextInput) {

//        return;

//    }

//

//    self.keyboardInfo = notification.userInfo;

//    [self adjustContainerViewOrigin];

}

- (void)keyboardWillHide:(NSNotification*)notification {

    self.keyboardInfo = nil;

    NSTimeInterval duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];

    UIViewAnimationCurve curve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];

    [UIView beginAnimations:nil context:NULL];

    [UIView setAnimationBeginsFromCurrentState:YES];

    [UIView setAnimationCurve:curve];

    [UIView setAnimationDuration:duration];

    self.containerView.transform = CGAffineTransformIdentity;

    [UIView commitAnimations];

}

- (UIView <UIKeyInput> *)getCurrentTextInputInView:(UIView *)view {

    if ([view conformsToProtocol:@protocol(UIKeyInput)] && view.isFirstResponder) {

        // Quick fix for web view issue

        if ([view isKindOfClass:NSClassFromString(@"UIWebBrowserView")] || [view isKindOfClass:NSClassFromString(@"WKContentView")]) {

            returnnil;

        }

        return(UIView *) view;

    }

    for(UIView*subviewinview.subviews) {

        UIView *inputInView = [selfgetCurrentTextInputInView:subview];

        if(inputInView) {

            returninputInView;

        }

    }

    return nil;

}

#pragma mark- touch event

- (void)didTapBackgroundView {

    if (self.shouldDismissOnBackgroundTouch) {

        [selfdismiss];

    }

}

#pragma mark- UI Layout

- (void)layoutContainerView {

    CGAffineTransform lastTransform = self.containerView.transform;

    self.containerView.transform = CGAffineTransformIdentity;

    self.backgroundView.frame = self.containerViewController.view.bounds;

    CGSizecontentSizeOfTopView = [selfcontentSizeOfTopView];

    CGFloatcontainerViewWidth = contentSizeOfTopView.width;

    CGFloatcontainerViewHeight = contentSizeOfTopView.height;

    CGFloatcontainerViewY;

    switch (self.popPosition) {

        case HWPopPositionBottom:{

            containerViewHeight +=_safeAreaInsets.bottom;

            containerViewY =self.containerViewController.view.bounds.size.height- containerViewHeight;

        }

            break;

        case HWPopPositionTop:{

            containerViewY =0;

        }

            break;

        default:{

            containerViewY = (self.containerViewController.view.bounds.size.height- containerViewHeight) /2;

        }

            break;

    }

    containerViewY +=self.positionOffset.y;

    CGFloatcontainerViewX = (self.containerViewController.view.bounds.size.width- containerViewWidth) /2+self.positionOffset.x;

    self.containerView.frame=CGRectMake(containerViewX, containerViewY, containerViewWidth, containerViewHeight);

    self.contentView.frame=CGRectMake(0,0, contentSizeOfTopView.width, contentSizeOfTopView.height);

    UIViewController*topViewController =self.topViewController;

    topViewController.view.frame=self.contentView.bounds;

    self.containerView.transform= lastTransform;

}

- (CGSize)contentSizeOfTopView {

    UIViewController*topViewController =self.topViewController;

    CGSizecontentSize;

    switch ([UIApplication sharedApplication].statusBarOrientation) {

        case UIInterfaceOrientationLandscapeLeft:

        case UIInterfaceOrientationLandscapeRight: {

            contentSize = topViewController.contentSizeInPopWhenLandscape;

            if(CGSizeEqualToSize(contentSize,CGSizeZero)) {

                contentSize = topViewController.contentSizeInPop;

            }

        }

            break;

        default: {

            contentSize = topViewController.contentSizeInPop;

        }

            break;

    }

    NSAssert(!CGSizeEqualToSize(contentSize, CGSizeZero), @"contentSizeInPopup should not be size zero.");

    returncontentSize;

}

#pragma mark- UI prepare

- (void)setup{

    self.shouldDismissOnBackgroundTouch = YES;

    self.animationDuration = 0.2;

    self.popType = HWPopTypeGrowIn;

    self.dismissType = HWDismissTypeFadeOut;


    [self.containerViewController.view addSubview:self.containerView];

    [self.containerView addSubview:self.contentView];

    UIView*bgView = [UIViewnew];

    self.backgroundView= bgView;

    self.backgroundAlpha = 0.5;

}

#pragma mark- Setter

- (void)setSafeAreaInsets:(UIEdgeInsets)safeAreaInsets {

    _safeAreaInsets= safeAreaInsets;

    self.didOverrideSafeAreaInsets = YES;

}

- (void)setBackgroundView:(UIView*)backgroundView {

    [_backgroundView removeFromSuperview];

    _backgroundView= backgroundView;

    [_backgroundView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapBackgroundView)]];

    [self.containerViewController.view insertSubview:_backgroundView atIndex:0];

}

- (void)setBackgroundAlpha:(CGFloat)backgroundAlpha {

    _backgroundAlpha= backgroundAlpha;

    self.backgroundView.backgroundColor = [UIColor colorWithWhite:0 alpha:backgroundAlpha];

}

#pragma mark- Getter

- (UIView *)containerView {

    if (!_containerView) {

        _containerView= [UIViewnew];

        _containerView.backgroundColor = [UIColor whiteColor];

        _containerView.clipsToBounds = YES;

        _containerView.layer.cornerRadius = 8;

    }

    return _containerView;

}

- (HWPopContainerViewController *)containerViewController {

    if (!_containerViewController) {

        _containerViewController = [HWPopContainerViewController new];

        _containerViewController.modalPresentationStyle = UIModalPresentationCustom;

        self.transitioningDelegate = [[HWPopTransitioningDelegate alloc] initWithPopController:self];

        _containerViewController.transitioningDelegate = self.transitioningDelegate;

    }

    return _containerViewController;

}

- (UIView *)contentView {

    if (!_contentView) {

        _contentView= [UIViewnew];

    }

    return _contentView;

}

- (BOOL)presented {

    return self.containerViewController.presentingViewController != nil;

}

- (void)dealloc {

    [self destroyObserver];

    [self destroyObserverOfViewController:self.topViewController];

}

@end

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容