text: //
  //  TYAlertController.m
  //  TYAlertControllerDemo
  //
  //  Created by tanyang on 15/9/1.
  //  Copyright (c) 2015年 tanyang. All rights reserved.
  //
  
  #import "TYAlertController.h"
  #import "UIView+TYAutoLayout.h"
  
  @interface TYAlertController ()
  
  @property (nonatomic, strong) UIView *alertView;
  
  @property (nonatomic, assign) TYAlertControllerStyle preferredStyle;
  
  @property (nonatomic, assign) TYAlertTransitionAnimation transitionAnimation;
  
  @property (nonatomic, assign) Class transitionAnimationClass;
  
  @property (nonatomic, weak) UITapGestureRecognizer *singleTap;
  
  @property (nonatomic, strong) NSLayoutConstraint *alertViewCenterYConstraint;
  
  @property (nonatomic, assign) CGFloat alertViewCenterYOffset;
  
  @end
  
  @implementation TYAlertController
  
  #pragma mark - init
  
  - (instancetype)init
  {
      if (self = [super init]) {
          [self configureController];
      }
      return self;
  }
  
  - (id)initWithCoder:(NSCoder *)aDecoder
  {
      if (self = [super initWithCoder:aDecoder]) {
          [self configureController];
      }
      return self;
  }
  
  - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  {
      if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
          [self configureController];
      }
      return self;
  }
  
  - (instancetype)initWithAlertView:(UIView *)alertView preferredStyle:(TYAlertControllerStyle)preferredStyle transitionAnimation:(TYAlertTransitionAnimation)transitionAnimation transitionAnimationClass:(Class)transitionAnimationClass
  {
      if (self = [self initWithNibName:nil bundle:nil]) {
          _alertView = alertView;
          _preferredStyle = preferredStyle;
          _transitionAnimation = transitionAnimation;
          _transitionAnimationClass = transitionAnimationClass;
      }
      return self;
  }
  
  + (instancetype)alertControllerWithAlertView:(UIView *)alertView
  {
      return [[self alloc]initWithAlertView:alertView
                             preferredStyle:TYAlertControllerStyleAlert
                        transitionAnimation:TYAlertTransitionAnimationFade
                   transitionAnimationClass:nil];
  }
  
  + (instancetype)alertControllerWithAlertView:(UIView *)alertView preferredStyle:(TYAlertControllerStyle)preferredStyle
  {
      return [[self alloc]initWithAlertView:alertView
                             preferredStyle:preferredStyle
                        transitionAnimation:TYAlertTransitionAnimationFade
                   transitionAnimationClass:nil];
  }
  
  + (instancetype)alertControllerWithAlertView:(UIView *)alertView preferredStyle:(TYAlertControllerStyle)preferredStyle transitionAnimation:(TYAlertTransitionAnimation)transitionAnimation
  {
      return [[self alloc]initWithAlertView:alertView
                             preferredStyle:preferredStyle
                        transitionAnimation:transitionAnimation
                   transitionAnimationClass:nil];
  }
  
  + (instancetype)alertControllerWithAlertView:(UIView *)alertView preferredStyle:(TYAlertControllerStyle)preferredStyle transitionAnimationClass:(Class)transitionAnimationClass
  {
      return [[self alloc]initWithAlertView:alertView
                             preferredStyle:preferredStyle
                        transitionAnimation:TYAlertTransitionAnimationCustom
                   transitionAnimationClass:transitionAnimationClass];
  }
  
  #pragma mark - life cycle
  
  - (void)viewDidLoad {
      [super viewDidLoad];
      // Do any additional setup after loading the view.
      self.view.backgroundColor = [UIColor clearColor];
      
      [self addBackgroundView];
      
      [self addSingleTapGesture];
      
      [self configureAlertView];
      
      [self.view layoutIfNeeded];
      
      if (_preferredStyle == TYAlertControllerStyleAlert) {
          // UIKeyboard Notification
          [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
          [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
      }
      
  }
  
  - (void)viewWillAppear:(BOOL)animated
  {
      [super viewWillAppear:animated];
      
      if (_viewWillShowHandler) {
          _viewWillShowHandler(_alertView);
      }
  }
  
  - (void)viewDidAppear:(BOOL)animated
  {
      [super viewDidAppear:animated];
      
      if (_viewDidShowHandler) {
          _viewDidShowHandler(_alertView);
      }
  }
  
  - (void)viewWillDisappear:(BOOL)animated
  {
      [super viewWillDisappear:animated];
      
      if (_viewWillHideHandler) {
          _viewWillHideHandler(_alertView);
      }
  }
  
  - (void)viewDidDisappear:(BOOL)animated
  {
      [super viewDidDisappear:animated];
      
      if (_viewDidHideHandler) {
          _viewDidHideHandler(_alertView);
      }
  }
  
  - (void)addBackgroundView
  {
      if (_backgroundView == nil) {
          UIView *backgroundView = [[UIView alloc]init];
          backgroundView.backgroundColor = _backgroundColor;
          _backgroundView = backgroundView;
      }
      _backgroundView.translatesAutoresizingMaskIntoConstraints = NO;
      [self.view insertSubview:_backgroundView atIndex:0];
      [self.view addConstraintToView:_backgroundView edgeInset:UIEdgeInsetsZero];
  }
  
  - (void)setBackgroundView:(UIView *)backgroundView
  {
      if (_backgroundView == nil) {
          _backgroundView = backgroundView;
      } else if (_backgroundView != backgroundView) {
          backgroundView.translatesAutoresizingMaskIntoConstraints = NO;
          [self.view insertSubview:backgroundView aboveSubview:_backgroundView];
          [self.view addConstraintToView:backgroundView edgeInset:UIEdgeInsetsZero];
          backgroundView.alpha = 0;
          [UIView animateWithDuration:0.3 animations:^{
              backgroundView.alpha = 1;
          } completion:^(BOOL finished) {
              [_backgroundView removeFromSuperview];
              _backgroundView = backgroundView;
              [self addSingleTapGesture];
          }];
      }
  }
  
  - (void)addSingleTapGesture
  {
      self.view.userInteractionEnabled = YES;
      _backgroundView.userInteractionEnabled = YES;
  
      UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
      singleTap.enabled = _backgoundTapDismissEnable;
    
      [_backgroundView addGestureRecognizer:singleTap];
      _singleTap = singleTap;
  }
  
  - (void)setBackgoundTapDismissEnable:(BOOL)backgoundTapDismissEnable
  {
      _backgoundTapDismissEnable = backgoundTapDismissEnable;
      _singleTap.enabled = backgoundTapDismissEnable;
  }
  
  #pragma mark - configure
  
  - (void)configureController
  {
      self.providesPresentationContextTransitionStyle = YES;
      self.definesPresentationContext = YES;
      self.modalPresentationStyle = UIModalPresentationCustom;
      self.transitioningDelegate = self;
      
      _backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.4];
      _backgoundTapDismissEnable = NO;
      _alertStyleEdging = 15;
      _actionSheetStyleEdging = 0;
  }
  
  - (void)configureAlertView
  {
      if (_alertView == nil) {
          NSLog(@"%@: alertView is nil",NSStringFromClass([self class]));
          return;
      }
      _alertView.userInteractionEnabled = YES;
      [self.view addSubview:_alertView];
      _alertView.translatesAutoresizingMaskIntoConstraints = NO;
      switch (_preferredStyle) {
          case TYAlertControllerStyleActionSheet:
              [self layoutActionSheetStyleView];
              break;
          case TYAlertControllerStyleAlert:
              [self layoutAlertStyleView];
              break;
          default:
              break;
      }
  }
  
  - (void)configureAlertViewWidth
  {
      // width, height
      if (!CGSizeEqualToSize(_alertView.frame.size,CGSizeZero)) {
          [_alertView addConstraintWidth:CGRectGetWidth(_alertView.frame) height:CGRectGetHeight(_alertView.frame)];
          
      }else {
          BOOL findAlertViewWidthConstraint = NO;
          for (NSLayoutConstraint *constraint in _alertView.constraints) {
              if (constraint.firstAttribute == NSLayoutAttributeWidth) {
                  findAlertViewWidthConstraint = YES;
                  break;
              }
          }
          
          if (!findAlertViewWidthConstraint) {
              [_alertView addConstraintWidth:CGRectGetWidth(self.view.frame)-2*_alertStyleEdging height:0];
          }
      }
  }
  
  #pragma mark - layout 
  
  - (void)layoutAlertStyleView
  {
      // center X
      [self.view addConstraintCenterXToView:_alertView centerYToView:nil];
      
      [self configureAlertViewWidth];
      
      // top Y
      _alertViewCenterYConstraint = [self.view addConstraintCenterYToView:_alertView constant:0];
      
      if (_alertViewOriginY > 0) {
          [_alertView layoutIfNeeded];
          _alertViewCenterYOffset = _alertViewOriginY - (CGRectGetHeight(self.view.frame) - CGRectGetHeight(_alertView.frame))/2;
          _alertViewCenterYConstraint.constant = _alertViewCenterYOffset;
      }else{
          _alertViewCenterYOffset = 0;
      }
  }
  
  - (void)layoutActionSheetStyleView
  {
      // remove width constaint
      for (NSLayoutConstraint *constraint in _alertView.constraints) {
          if (constraint.firstAttribute == NSLayoutAttributeWidth) {
              [_alertView removeConstraint: constraint];
              break;
          }
      }
      
      // add edge constraint
      [self.view addConstraintWithView:_alertView topView:nil leftView:self.view bottomView:self.view rightView:self.view edgeInset:UIEdgeInsetsMake(0, _actionSheetStyleEdging, 0, -_actionSheetStyleEdging)];
      
      if (CGRectGetHeight(_alertView.frame) > 0) {
          // height
          [_alertView addConstraintWidth:0 height:CGRectGetHeight(_alertView.frame)];
      }
  }
  
  - (void)dismissViewControllerAnimated:(BOOL)animated
  {
      [self dismissViewControllerAnimated:YES completion:self.dismissComplete];
  }
  
  #pragma mark - action
  
  - (void)singleTap:(UITapGestureRecognizer *)sender
  {
      [self dismissViewControllerAnimated:YES];
  }
  
  #pragma mark - notification
  
  - (void)keyboardWillShow:(NSNotification*)notification
  {
      CGRect keyboardRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
      
      CGFloat alertViewBottomEdge = (CGRectGetHeight(self.view.frame) -  CGRectGetHeight(_alertView.frame))/2 - _alertViewCenterYOffset;
      
      //当开启热点时，向下偏移20px
      //修复键盘遮挡问题
      CGFloat statusBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height;
      CGFloat differ = CGRectGetHeight(keyboardRect) - alertViewBottomEdge;
  
      //修复：输入框获取焦点时，会重复刷新，导致输入框文章偏移一下
      if (_alertViewCenterYConstraint.constant == -differ -statusBarHeight) {
          return;
      }
      
      if (differ >= 0) {
           _alertViewCenterYConstraint.constant = _alertViewCenterYOffset - differ - statusBarHeight;
          [UIView animateWithDuration:0.25 animations:^{
              [self.view layoutIfNeeded];
          }];
      }
  }
  
  
  - (void)keyboardWillHide:(NSNotification*)notification
  {
      _alertViewCenterYConstraint.constant = _alertViewCenterYOffset;
      [UIView animateWithDuration:0.25 animations:^{
          [self.view layoutIfNeeded];
      }];
  }
  
  - (void)didReceiveMemoryWarning {
      [super didReceiveMemoryWarning];
      // Dispose of any resources that can be recreated.
  }
  
  - (void)dealloc
  {
      [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
      [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
  }
  
  @end
  