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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乞旦,一起剝皮案震驚了整個(gè)濱河市钱慢,隨后出現(xiàn)的幾起案子身辨,更是在濱河造成了極大的恐慌,老刑警劉巖汹桦,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡臊泰,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門蚜枢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缸逃,“玉大人七婴,你說我怎么就攤上這事〔旎” “怎么了打厘?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)贺辰。 經(jīng)常有香客問我户盯,道長(zhǎng),這世上最難降的妖魔是什么饲化? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任莽鸭,我火速辦了婚禮,結(jié)果婚禮上吃靠,老公的妹妹穿的比我還像新娘硫眨。我一直安慰自己,他們只是感情好巢块,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布礁阁。 她就那樣靜靜地躺著,像睡著了一般族奢。 火紅的嫁衣襯著肌膚如雪姥闭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天越走,我揣著相機(jī)與錄音棚品,去河邊找鬼。 笑死廊敌,一個(gè)胖子當(dāng)著我的面吹牛铜跑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播骡澈,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼锅纺,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了秧廉?” 一聲冷哼從身側(cè)響起伞广,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疼电,沒想到半個(gè)月后嚼锄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蔽豺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年区丑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡沧侥,死狀恐怖可霎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宴杀,我是刑警寧澤癣朗,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站旺罢,受9級(jí)特大地震影響旷余,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扁达,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一正卧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧跪解,春花似錦背桐、人聲如沸兽肤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至节吮,卻和暖如春抽高,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背透绩。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留壁熄,地道東北人帚豪。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像草丧,于是被迫代替她去往敵國和親狸臣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容