diff --git a/NYAlertViewController.podspec b/NYAlertViewController.podspec index 6ad55e4..58da441 100644 --- a/NYAlertViewController.podspec +++ b/NYAlertViewController.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "NYAlertViewController" - s.version = "1.3.0" + s.version = "1.4.0" s.summary = "Highly Customizable iOS Alert Views" s.description = "Replacement for UIAlertController/UIAlertView with support for content views and UI customization" s.homepage = "https://github.com/nealyoung/NYAlertViewController" @@ -9,7 +9,12 @@ Pod::Spec.new do |s| s.author = { "Neal Young" => "hi@nealyoung.me" } s.social_media_url = "http://nealyoung.me" s.platform = :ios, "8.0" - s.source = { :git => "https://github.com/nealyoung/NYAlertViewController.git", :tag => "#{s.version}" } + s.source = { :git => "https://github.com/schmidt9/NYAlertViewController.git", :tag => "#{s.version}" } s.source_files = "NYAlertViewController/*.{h,m}" s.requires_arc = true + + s.pod_target_xcconfig = { + # fix for "'sharedApplication' is unavailable: not available on iOS (App Extension)" + "APPLICATION_EXTENSION_API_ONLY" => "NO" + } end diff --git a/NYAlertViewController/NYAlertView.h b/NYAlertViewController/NYAlertView.h index f220c85..be2001b 100644 --- a/NYAlertViewController/NYAlertView.h +++ b/NYAlertViewController/NYAlertView.h @@ -12,24 +12,40 @@ typedef NS_ENUM(NSInteger, NYAlertViewButtonType) { NYAlertViewButtonTypeBordered }; +typedef NS_ENUM(NSInteger, NYAlertViewStyle) { + NYAlertViewStyleDefault, + NYAlertViewStyleIOSCustom +}; + @interface UIButton (BackgroundColor) - (void)setBackgroundColor:(UIColor *)color forState:(UIControlState)state; @end +@interface NYAlertTextView : UITextView +@end + @interface NYAlertViewButton : UIButton @property (nonatomic) NYAlertViewButtonType type; +@property (nonatomic) UIColor *borderColor; +@property (nonatomic) UIColor *borderedTitleColor; + +@property (nonatomic) CGFloat borderWidth; @property (nonatomic) CGFloat cornerRadius; @end @interface NYAlertView : UIView -@property UILabel *titleLabel; -@property UITextView *messageTextView; +- (instancetype)initWithStyle:(NYAlertViewStyle)style; + +@property (nonatomic) NYAlertViewStyle style; + +@property (nonatomic) UILabel *titleLabel; +@property (nonatomic) NYAlertTextView *messageTextView; @property (nonatomic) UIView *contentView; @property (nonatomic) UIFont *buttonTitleFont; @@ -43,16 +59,27 @@ typedef NS_ENUM(NSInteger, NYAlertViewButtonType) { @property (nonatomic) UIColor *destructiveButtonColor; @property (nonatomic) UIColor *destructiveButtonTitleColor; +@property (nonatomic) NSString *title; @property (nonatomic) CGFloat buttonCornerRadius; @property (nonatomic) CGFloat maximumWidth; +/** + * Shows content in full screen mode (but not covering status bar). + */ +@property (nonatomic) BOOL isFullScreen; @property (nonatomic, readonly) UIView *alertBackgroundView; @property (nonatomic, readonly) NSLayoutConstraint *backgroundViewVerticalCenteringConstraint; +@property (nonatomic, readonly) NSLayoutConstraint *messageTextViewHeightConstraint; //@property (nonatomic) NSArray *actions; -@property (nonatomic) NSArray *actionButtons; +@property (nonatomic) NSArray *actionButtons; + +@property (nonatomic) NSArray *textFields; + +/// View from contentView which should be visible, overrides first responder / active text field +@property (nonatomic) UIView *keyboardEscapingView; -@property (nonatomic) NSArray *textFields; +- (void)updateMessageTextViewHeight; @end diff --git a/NYAlertViewController/NYAlertView.m b/NYAlertViewController/NYAlertView.m index 227d52f..e53bab3 100644 --- a/NYAlertViewController/NYAlertView.m +++ b/NYAlertViewController/NYAlertView.m @@ -7,25 +7,21 @@ #import "NYAlertView.h" -#import "NYAlertViewController.h" -@interface NYAlertTextView : UITextView +static NSString * const kBackgroundViewHeightConstraintIdentifier = @"kBackgroundViewHeightConstraintIdentifier"; -@end @implementation NYAlertTextView - (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer { self = [super initWithFrame:frame textContainer:textContainer]; - self.textContainerInset = UIEdgeInsetsZero; - return self; } - (void)layoutSubviews { [super layoutSubviews]; - + if (!CGSizeEqualToSize(self.bounds.size, [self intrinsicContentSize])) { [self invalidateIntrinsicContentSize]; } @@ -51,13 +47,13 @@ - (UIImage *)imageWithColor:(UIColor *)color { CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f); UIGraphicsBeginImageContext(rect.size); CGContextRef context = UIGraphicsGetCurrentContext(); - + CGContextSetFillColorWithColor(context, [color CGColor]); CGContextFillRect(context, rect); - + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); - + return image; } @@ -71,37 +67,37 @@ + (id)buttonWithType:(UIButtonType)buttonType { - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; - + if (self) { [self commonInit]; } - + return self; } - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; - + if (self) { [self commonInit]; } - + return self; } - (void)commonInit { self.layer.rasterizationScale = [[UIScreen mainScreen] scale]; self.layer.shouldRasterize = YES; - + self.layer.borderWidth = 1.0f; - + self.cornerRadius = 4.0f; self.clipsToBounds = YES; - + [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; [self setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted]; [self setTitleColor:[UIColor whiteColor] forState:UIControlStateDisabled]; - + [self tintColorDidChange]; } @@ -112,7 +108,7 @@ - (void)setHidden:(BOOL)hidden { - (void)setEnabled:(BOOL)enabled { [super setEnabled:enabled]; - + // if (!enabled) { // self.backgroundColor = [UIColor lightGrayColor]; // self.layer.borderColor = self.tintColor.CGColor; @@ -126,7 +122,7 @@ - (void)setEnabled:(BOOL)enabled { - (void)tintColorDidChange { [super tintColorDidChange]; - + if (self.type == NYAlertViewButtonTypeFilled) { if (self.enabled) { [self setBackgroundColor:self.tintColor]; @@ -134,9 +130,9 @@ - (void)tintColorDidChange { } else { [self setTitleColor:self.tintColor forState:UIControlStateNormal]; } - + self.layer.borderColor = self.tintColor.CGColor; - + [self setNeedsDisplay]; } @@ -148,35 +144,11 @@ - (void)setCornerRadius:(CGFloat)cornerRadius { self.layer.cornerRadius = cornerRadius; } -//- (void)setEnabled:(BOOL)enabled { -// [super setEnabled:enabled]; -// -// if (enabled) { -// self.layer.backgroundColor = self.tintColor.CGColor; -// [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; -// } else { -// self.layer.backgroundColor = [UIColor lightGrayColor].CGColor; -// [self setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal]; -// } -//} - -//- (void)setType:(NYAlertViewButtonType)type { -// _type = type; -// -// if (type == NYAlertViewButtonTypeBordered) { -// self.layer.backgroundColor = [UIColor clearColor].CGColor; -// [self setTitleColor:self.tintColor forState:UIControlStateNormal]; -// } else { -// self.layer.backgroundColor = self.tintColor.CGColor; -// [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; -// } -//} - - (CGSize)intrinsicContentSize { if (self.hidden) { return CGSizeZero; } - + return CGSizeMake([super intrinsicContentSize].width + 12.0f, 30.0f); } @@ -197,22 +169,22 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - (void)drawRect:(CGRect)rect { [super drawRect:rect]; - - self.layer.borderColor = self.tintColor.CGColor; - + + self.layer.borderColor = _borderColor ? _borderColor.CGColor : self.tintColor.CGColor; + if (self.type == NYAlertViewButtonTypeBordered) { - self.layer.borderWidth = 1.0f; + self.layer.borderWidth = (_borderWidth > 0) ? _borderWidth : 1.0f; } else { self.layer.borderWidth = 0.0f; } - + if (self.state == UIControlStateHighlighted) { self.layer.backgroundColor = self.tintColor.CGColor; // [self setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted]; } else { if (self.type == NYAlertViewButtonTypeBordered) { self.layer.backgroundColor = nil; - [self setTitleColor:self.tintColor forState:UIControlStateNormal]; + [self setTitleColor:_borderedTitleColor ? _borderedTitleColor : self.tintColor forState:UIControlStateNormal]; } else { // [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; } @@ -224,9 +196,15 @@ - (void)drawRect:(CGRect)rect { @interface NYAlertView () @property (nonatomic) NSLayoutConstraint *alertBackgroundWidthConstraint; +@property (nonatomic) NSLayoutConstraint *alertBackgroundHeightConstraint; +@property (nonatomic) NSLayoutConstraint *messageTextViewTopConstraint; +@property (nonatomic) NSLayoutConstraint *textFieldContainerViewHeightConstraint; @property (nonatomic) UIView *contentViewContainerView; @property (nonatomic) UIView *textFieldContainerView; @property (nonatomic) UIView *actionButtonContainerView; +@property (nonatomic) UITextField *activeTextField; +@property (nonatomic) BOOL keyboardIsVisible; +@property (nonatomic) CGSize keyboardSize; @end @@ -234,133 +212,252 @@ @implementation NYAlertView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; - + if (self) { - self.maximumWidth = 480.0f; - - _alertBackgroundView = [[UIView alloc] initWithFrame:CGRectZero]; - [self.alertBackgroundView setTranslatesAutoresizingMaskIntoConstraints:NO]; - self.alertBackgroundView.backgroundColor = [UIColor colorWithWhite:0.97f alpha:1.0f]; - self.alertBackgroundView.layer.cornerRadius = 6.0f; - [self addSubview:_alertBackgroundView]; - - _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; - [self.titleLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; - self.titleLabel.numberOfLines = 2; - self.titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]; - self.titleLabel.textAlignment = NSTextAlignmentCenter; - self.titleLabel.textColor = [UIColor darkGrayColor]; - self.titleLabel.text = NSLocalizedString(@"Title Label", nil); - [self.alertBackgroundView addSubview:self.titleLabel]; - - _messageTextView = [[NYAlertTextView alloc] initWithFrame:CGRectZero]; - [self.messageTextView setTranslatesAutoresizingMaskIntoConstraints:NO]; - self.messageTextView.backgroundColor = [UIColor clearColor]; - [self.messageTextView setContentHuggingPriority:0 forAxis:UILayoutConstraintAxisVertical]; - [self.messageTextView setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical]; - self.messageTextView.editable = NO; - self.messageTextView.textAlignment = NSTextAlignmentCenter; - self.messageTextView.textColor = [UIColor darkGrayColor]; - self.messageTextView.font = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]; - self.messageTextView.text = NSLocalizedString(@"Message Text View", nil); - [self.alertBackgroundView addSubview:self.messageTextView]; - - _contentViewContainerView = [[UIView alloc] initWithFrame:CGRectZero]; - [self.contentViewContainerView setTranslatesAutoresizingMaskIntoConstraints:NO]; - [self.contentView setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; - [self.alertBackgroundView addSubview:self.contentViewContainerView]; - - _textFieldContainerView = [[UIView alloc] initWithFrame:CGRectZero]; - [self.textFieldContainerView setTranslatesAutoresizingMaskIntoConstraints:NO]; - [self.textFieldContainerView setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; - [self.alertBackgroundView addSubview:self.textFieldContainerView]; - - _actionButtonContainerView = [[UIView alloc] initWithFrame:CGRectZero]; - [self.actionButtonContainerView setTranslatesAutoresizingMaskIntoConstraints:NO]; - [self.actionButtonContainerView setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; - [self.alertBackgroundView addSubview:self.actionButtonContainerView]; - - [self addConstraint:[NSLayoutConstraint constraintWithItem:self.alertBackgroundView - attribute:NSLayoutAttributeCenterX - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeCenterX - multiplier:1.0f - constant:0.0f]]; - - CGFloat alertBackgroundViewWidth = MIN(CGRectGetWidth([UIApplication sharedApplication].keyWindow.bounds), - CGRectGetHeight([UIApplication sharedApplication].keyWindow.bounds)) * 0.8f; - - if (alertBackgroundViewWidth > self.maximumWidth) { - alertBackgroundViewWidth = self.maximumWidth; - } - - _alertBackgroundWidthConstraint = [NSLayoutConstraint constraintWithItem:self.alertBackgroundView - attribute:NSLayoutAttributeWidth - relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:0.0f - constant:alertBackgroundViewWidth]; - - [self addConstraint:self.alertBackgroundWidthConstraint]; - - _backgroundViewVerticalCenteringConstraint = [NSLayoutConstraint constraintWithItem:self.alertBackgroundView - attribute:NSLayoutAttributeCenterY - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeCenterY - multiplier:1.0f - constant:0.0f]; - - [self addConstraint:self.backgroundViewVerticalCenteringConstraint]; - - [self addConstraint:[NSLayoutConstraint constraintWithItem:self.alertBackgroundView - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationLessThanOrEqual - toItem:self - attribute:NSLayoutAttributeHeight - multiplier:0.9f - constant:0.0f]]; - - [self.alertBackgroundView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_titleLabel]-|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(_titleLabel)]]; - - [self.alertBackgroundView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_messageTextView]-|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(_messageTextView)]]; - - [self.alertBackgroundView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_contentViewContainerView]|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(_contentViewContainerView)]]; - - [self.alertBackgroundView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_textFieldContainerView]|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(_textFieldContainerView)]]; - - [self.alertBackgroundView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_actionButtonContainerView]|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(_actionButtonContainerView)]]; - - [self.alertBackgroundView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[_titleLabel]-2-[_messageTextView][_contentViewContainerView][_textFieldContainerView][_actionButtonContainerView]-|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(_titleLabel, - _messageTextView, - _contentViewContainerView, - _textFieldContainerView, - _actionButtonContainerView)]]; + [self commonInit]; + } + + return self; +} + +- (instancetype)initWithStyle:(NYAlertViewStyle)style +{ + self = [super initWithFrame:CGRectZero]; + + if (self) { + self.style = style; + [self commonInit]; } - + return self; } +- (void)layoutSubviews +{ + [super layoutSubviews]; + // call here to get actual text view content width and calculate height + // downside of this approach: layoutSubviews is being intensively called on text view scrolling + // possible workaround: disable text view scrolling for small messages + [self updateMessageTextViewHeight]; +} + +- (void)commonInit +{ + CGFloat screenWidth = CGRectGetWidth(UIScreen.mainScreen.bounds); + CGFloat screenHeight = CGRectGetHeight(UIScreen.mainScreen.bounds); + + self.maximumWidth = screenWidth; + + _alertBackgroundView = [[UIView alloc] initWithFrame:CGRectZero]; + [self.alertBackgroundView setTranslatesAutoresizingMaskIntoConstraints:NO]; + self.alertBackgroundView.backgroundColor = [UIColor colorWithWhite:0.97f alpha:1.0f]; + self.alertBackgroundView.layer.cornerRadius = 6.0f; + self.alertBackgroundView.accessibilityIdentifier = @"alertBackgroundView"; + + // keep just default bottom inset to add padding to action buttons when view is in full screen mode + if (@available(iOS 11, *)) { + CGFloat bottomInset = self.alertBackgroundView.directionalLayoutMargins.bottom; + self.alertBackgroundView.directionalLayoutMargins = NSDirectionalEdgeInsetsMake(0, 0, bottomInset, 0); + } else { + CGFloat bottomInset = self.alertBackgroundView.layoutMargins.bottom; + self.alertBackgroundView.layoutMargins = UIEdgeInsetsMake(0, 0, bottomInset, 0); + } + + [self addSubview:_alertBackgroundView]; + + _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + [self.titleLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; + self.titleLabel.numberOfLines = 2; + self.titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]; + self.titleLabel.textAlignment = NSTextAlignmentCenter; + self.titleLabel.textColor = [UIColor darkGrayColor]; + self.titleLabel.text = NSLocalizedString(@"Title Label", nil); + self.titleLabel.accessibilityIdentifier = @"titleLabel"; + [self.alertBackgroundView addSubview:self.titleLabel]; + + _messageTextView = [[NYAlertTextView alloc] initWithFrame:CGRectZero]; + [self.messageTextView setTranslatesAutoresizingMaskIntoConstraints:NO]; + self.messageTextView.backgroundColor = [UIColor clearColor]; + [self.messageTextView setContentHuggingPriority:0 forAxis:UILayoutConstraintAxisVertical]; + [self.messageTextView setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisVertical]; + self.messageTextView.editable = NO; + self.messageTextView.textAlignment = NSTextAlignmentCenter; + self.messageTextView.textColor = [UIColor darkGrayColor]; + self.messageTextView.font = [UIFont systemFontOfSize:16]; + self.messageTextView.textContainerInset = (self.style == NYAlertViewStyleIOSCustom) + ? UIEdgeInsetsMake(7, 0, 0, 0) + : self.messageTextView.textContainerInset; + self.messageTextView.accessibilityIdentifier = @"messageTextView"; + [self.alertBackgroundView addSubview:self.messageTextView]; + + _messageTextViewHeightConstraint = [NSLayoutConstraint + constraintWithItem:self.messageTextView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:0]; + // set low priority to allow breaking height if it is too big (higher than screen height) + self.messageTextViewHeightConstraint.priority = UILayoutPriorityDefaultLow; + [self.messageTextView addConstraint:self.messageTextViewHeightConstraint]; + + _contentViewContainerView = [[UIView alloc] initWithFrame:CGRectZero]; + [self.contentViewContainerView setTranslatesAutoresizingMaskIntoConstraints:NO]; + [self.contentView setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; + self.contentViewContainerView.accessibilityIdentifier = @"contentViewContainerView"; + [self.alertBackgroundView addSubview:self.contentViewContainerView]; + + _textFieldContainerView = [[UIView alloc] initWithFrame:CGRectZero]; + [self.textFieldContainerView setTranslatesAutoresizingMaskIntoConstraints:NO]; + [self.textFieldContainerView setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; + [self.alertBackgroundView addSubview:self.textFieldContainerView]; + self.textFieldContainerViewHeightConstraint = [NSLayoutConstraint + constraintWithItem:self.textFieldContainerView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:0]; + self.textFieldContainerView.accessibilityIdentifier = @"textFieldContainerView"; + [self.textFieldContainerView addConstraint:self.textFieldContainerViewHeightConstraint]; + + _actionButtonContainerView = [UIView new]; + [self.actionButtonContainerView setTranslatesAutoresizingMaskIntoConstraints:NO]; + [self.actionButtonContainerView setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; + self.actionButtonContainerView.accessibilityIdentifier = @"actionButtonContainerView"; + [self.alertBackgroundView addSubview:self.actionButtonContainerView]; + + UIView *topSeparatorView; + UIView *bottomSeparatorView; + + if (self.style == NYAlertViewStyleIOSCustom) { + topSeparatorView = [UIView new]; + [topSeparatorView setTranslatesAutoresizingMaskIntoConstraints:NO]; + topSeparatorView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.1]; + topSeparatorView.accessibilityIdentifier = @"topSeparatorView"; + [self.alertBackgroundView addSubview:topSeparatorView]; + + bottomSeparatorView = [UIView new]; + [bottomSeparatorView setTranslatesAutoresizingMaskIntoConstraints:NO]; + bottomSeparatorView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.1]; + bottomSeparatorView.accessibilityIdentifier = @"bottomSeparatorView"; + [self.alertBackgroundView addSubview:bottomSeparatorView]; + } + + [self addConstraint:[NSLayoutConstraint constraintWithItem:self.alertBackgroundView + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeCenterX + multiplier:1.0f + constant:0.0f]]; + + self.isFullScreen = NO; // add width and height constraints via setter + + _backgroundViewVerticalCenteringConstraint = + [NSLayoutConstraint constraintWithItem:self.alertBackgroundView + attribute:NSLayoutAttributeCenterY + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeCenterY + multiplier:1.0f + constant:0.0f]; + + [self addConstraint:self.backgroundViewVerticalCenteringConstraint]; + + [self.alertBackgroundView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:|-[_titleLabel]-|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_titleLabel)]]; + + [self.alertBackgroundView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:|-8-[_messageTextView]-8-|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_messageTextView)]]; + + [self.alertBackgroundView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:|[_contentViewContainerView]|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_contentViewContainerView)]]; + + [self.alertBackgroundView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:|[_textFieldContainerView]|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_textFieldContainerView)]]; + + [self.alertBackgroundView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:|[_actionButtonContainerView]|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_actionButtonContainerView)]]; + + NSString *format = (self.style == NYAlertViewStyleIOSCustom) + ? @"V:|-[_titleLabel]-[topSeparatorView]-0-[_messageTextView][_contentViewContainerView][_textFieldContainerView][bottomSeparatorView][_actionButtonContainerView]-0-|" + : @"V:|-[_titleLabel]-0-[_messageTextView][_contentViewContainerView][_textFieldContainerView][_actionButtonContainerView]-|"; + NSDictionary *views = (topSeparatorView) + ? NSDictionaryOfVariableBindings(_titleLabel, topSeparatorView, _messageTextView, _contentViewContainerView, _textFieldContainerView, bottomSeparatorView, _actionButtonContainerView) + : NSDictionaryOfVariableBindings(_titleLabel, _messageTextView, _contentViewContainerView, _textFieldContainerView, _actionButtonContainerView); + + NSArray *verticalConstraints = [NSLayoutConstraint + constraintsWithVisualFormat:format + options:0 + metrics:nil + views:views]; + [self.alertBackgroundView addConstraints:verticalConstraints]; + + for (NSLayoutConstraint *constraint in verticalConstraints) { + if ([constraint.firstItem isEqual:self.messageTextView] && [constraint.secondItem isEqual:self.titleLabel]) { + self.messageTextViewTopConstraint = constraint; + break; + } + } + + if (self.style == NYAlertViewStyleIOSCustom) { + [self.alertBackgroundView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:[topSeparatorView(1)]" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(topSeparatorView)]]; + [self.alertBackgroundView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:|-0-[topSeparatorView]-0-|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(topSeparatorView)]]; + [self.alertBackgroundView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:[bottomSeparatorView(1)]" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(bottomSeparatorView)]]; + [self.alertBackgroundView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:|-0-[bottomSeparatorView]-0-|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(bottomSeparatorView)]]; + } + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(keyboardWillShowNotification:) + name:UIKeyboardWillShowNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(keyboardWillHideNotification:) + name:UIKeyboardWillHideNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(textFieldDidBeginEditingNotification:) + name:UITextFieldTextDidBeginEditingNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(textFieldDidEndEditingNotification:) + name:UITextFieldTextDidEndEditingNotification + object:nil]; +} // Pass through touches outside the backgroundView for the presentation controller to handle dismissal - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { @@ -369,199 +466,393 @@ - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { return YES; } } - + return NO; } +- (void)setTitle:(NSString *)title { + _title = title; + self.titleLabel.text = title; + [self updateMessageTextViewHeight]; +} + - (void)setMaximumWidth:(CGFloat)maximumWidth { _maximumWidth = maximumWidth; self.alertBackgroundWidthConstraint.constant = maximumWidth; } +- (void)setIsFullScreen:(BOOL)isFullScreen +{ + _isFullScreen = isFullScreen; + + self.alertBackgroundView.layer.cornerRadius = isFullScreen ? 0 : 6.0f; + + CGFloat screenWidth = CGRectGetWidth(UIScreen.mainScreen.bounds); + CGFloat screenHeight = CGRectGetHeight(UIScreen.mainScreen.bounds) - self.statusBarHeight; + + CGFloat alertBackgroundViewWidth = MIN(screenWidth, screenHeight) * (isFullScreen ? 1.0f : 0.8f); + + if (self.alertBackgroundWidthConstraint) { + [self removeConstraint:self.alertBackgroundWidthConstraint]; + } + + self.alertBackgroundWidthConstraint = + [NSLayoutConstraint constraintWithItem:self.alertBackgroundView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:0.0f + constant:alertBackgroundViewWidth]; + + [self addConstraint:self.alertBackgroundWidthConstraint]; + + void (^addRelationalConstraint)() = ^{ + self.alertBackgroundHeightConstraint = + [NSLayoutConstraint constraintWithItem:self.alertBackgroundView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationLessThanOrEqual + toItem:self + attribute:NSLayoutAttributeHeight + multiplier:0.87f + constant:0.0f]; + + self.alertBackgroundHeightConstraint.identifier = kBackgroundViewHeightConstraintIdentifier; + [self addConstraint:self.alertBackgroundHeightConstraint]; + [self updateBackgroundViewVerticalCenteringConstraint]; + }; + + if (!self.alertBackgroundHeightConstraint) { + addRelationalConstraint(); + return; + } + + for (NSLayoutConstraint *constraint in self.constraints) { + if ([constraint.identifier isEqualToString:kBackgroundViewHeightConstraintIdentifier]) { + + [self removeConstraint:constraint]; + + if (isFullScreen) { + CGFloat alertBackgroundViewHeight = MAX(screenWidth, screenHeight); + + self.alertBackgroundHeightConstraint = + [NSLayoutConstraint constraintWithItem:self.alertBackgroundView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:0.0f + constant:alertBackgroundViewHeight]; + + self.alertBackgroundHeightConstraint.identifier = kBackgroundViewHeightConstraintIdentifier; + [self addConstraint:self.alertBackgroundHeightConstraint]; + [self updateBackgroundViewVerticalCenteringConstraint]; + } else { + addRelationalConstraint(); + } + + break; + } + } +} + - (void)setContentView:(UIView *)contentView { [self.contentView removeFromSuperview]; - + _contentView = contentView; - + _contentView.accessibilityIdentifier = @"contentView"; + if (contentView) { [self.contentView setTranslatesAutoresizingMaskIntoConstraints:NO]; [self.contentViewContainerView addSubview:self.contentView]; - - [self.contentViewContainerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_contentView]|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(_contentView)]]; - - [self.contentViewContainerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-2-[_contentView]-2-|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(_contentView)]]; + + [self.contentViewContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:|[_contentView]|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_contentView)]]; + + NSString *visualFormat = [NSString stringWithFormat:@"V:|-%ld-[_contentView]-2-|", self.isFullScreen ? 0 : 2]; + [self.contentViewContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:visualFormat + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_contentView)]]; } } -//- (void)actionButtonPressed:(NYAlertViewButton *)button { -// NYAlertAction *action = self.actions[button.tag]; -// action.handler(action); -//} - -//- (void)setActions:(NSArray *)actions { -//// _actions = actions; -//// -// NSMutableArray *buttons = [NSMutableArray array]; -// -// // Create buttons for each action -// for (int i = 0; i < [actions count]; i++) { -// UIAlertAction *action = actions[i]; -// -// NYAlertViewButton *button = [[NYAlertViewButton alloc] initWithFrame:CGRectZero]; -// -// button.tag = i; -// [button addTarget:self action:@selector(actionButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; -// -// button.cornerRadius = self.buttonCornerRadius; -// [button setTranslatesAutoresizingMaskIntoConstraints:NO]; -// [button setTitle:action.title forState:UIControlStateNormal]; -// -// if (action.style == UIAlertActionStyleCancel) { -// [button setTitleColor:self.cancelButtonTitleColor forState:UIControlStateNormal]; -// [button setTitleColor:self.cancelButtonTitleColor forState:UIControlStateHighlighted]; -// button.tintColor = self.cancelButtonColor; -// button.titleLabel.font = self.cancelButtonTitleFont; -// } else if (action.style == UIAlertActionStyleDestructive) { -// [button setTitleColor:self.destructiveButtonTitleColor forState:UIControlStateNormal]; -// [button setTitleColor:self.destructiveButtonTitleColor forState:UIControlStateHighlighted]; -// button.tintColor = self.destructiveButtonColor; -// button.titleLabel.font = self.destructiveButtonTitleFont; -// } else { -// [button setTitleColor:self.buttonTitleColor forState:UIControlStateNormal]; -// [button setTitleColor:self.buttonTitleColor forState:UIControlStateHighlighted]; -// button.tintColor = self.buttonColor; -// button.titleLabel.font = self.buttonTitleFont; -// } -// -// [buttons addObject:button]; -// } -// -// self.actionButtons = buttons; -//} - -- (void)setTextFields:(NSArray *)textFields { +- (void)setTextFields:(NSArray *)textFields { for (UITextField *textField in self.textFields) { [textField removeFromSuperview]; } - + _textFields = textFields; - + + NSInteger totalHeight = 0; + for (int i = 0; i < [textFields count]; i++) { UITextField *textField = textFields[i]; + + totalHeight += [textField sizeThatFits:CGSizeZero].height; + [textField setTranslatesAutoresizingMaskIntoConstraints:NO]; [self.textFieldContainerView addSubview:textField]; - - [self.textFieldContainerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[textField]-|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(textField)]]; - + + [self.textFieldContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:|-[textField]-|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(textField)]]; + // Pin the first text field to the top of the text field container view if (i == 0) { - [self.textFieldContainerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[textField]" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(_contentViewContainerView, textField)]]; + [self.textFieldContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:|-[textField]" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_contentViewContainerView, textField)]]; } else { UITextField *previousTextField = textFields[i - 1]; - - [self.textFieldContainerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[previousTextField]-[textField]" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(previousTextField, textField)]]; + + [self.textFieldContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:[previousTextField]-[textField]" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(previousTextField, textField)]]; } - + // Pin the final text field to the bottom of the text field container view if (i == ([textFields count] - 1)) { - [self.textFieldContainerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[textField]|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(textField)]]; + [self.textFieldContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:[textField]|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(textField)]]; } } + + self.textFieldContainerViewHeightConstraint.constant = totalHeight; } - (void)setActionButtons:(NSArray *)actionButtons { - for (UIButton *button in self.actionButtons) { + for (UIButton *button in self.actionButtons) { [button removeFromSuperview]; } - + + self.alertBackgroundView.clipsToBounds = (self.style == NYAlertViewStyleIOSCustom); + _actionButtons = actionButtons; - + // If there are 2 actions, display the buttons next to each other. Otherwise, stack the buttons vertically at full width if ([actionButtons count] == 2) { UIButton *firstButton = actionButtons[0]; UIButton *lastButton = actionButtons[1]; - + UIView *separatorView; + [self.actionButtonContainerView addSubview:firstButton]; [self.actionButtonContainerView addSubview:lastButton]; - - [self.actionButtonContainerView addConstraint:[NSLayoutConstraint constraintWithItem:firstButton - attribute:NSLayoutAttributeWidth - relatedBy:NSLayoutRelationEqual - toItem:lastButton - attribute:NSLayoutAttributeWidth - multiplier:1.0f - constant:0.0f]]; - - [self.actionButtonContainerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[firstButton]-[lastButton]-|" - options:NSLayoutFormatAlignAllCenterY - metrics:nil - views:NSDictionaryOfVariableBindings(firstButton, lastButton)]]; - - [self.actionButtonContainerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[firstButton(40)]|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(_contentViewContainerView, firstButton)]]; - - [self.actionButtonContainerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[lastButton(40)]" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(lastButton)]]; + + if (self.style == NYAlertViewStyleIOSCustom) { + separatorView = [UIView new]; + [separatorView setTranslatesAutoresizingMaskIntoConstraints:NO]; + separatorView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.1]; + [self.actionButtonContainerView addSubview:separatorView]; + + [self.actionButtonContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:[separatorView(1)]" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(separatorView)]]; + [self.actionButtonContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:|-0-[separatorView]-0-|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(separatorView)]]; + } + + [self.actionButtonContainerView addConstraint:[NSLayoutConstraint + constraintWithItem:firstButton + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:lastButton + attribute:NSLayoutAttributeWidth + multiplier:1.0f + constant:0.0f]]; + + NSString *format = (self.style == NYAlertViewStyleIOSCustom) + ? @"H:|-0-[firstButton]-0-[separatorView]-0-[lastButton]-0-|" + : @"H:|-[firstButton]-[lastButton]-|"; + + NSDictionary *views = (self.style == NYAlertViewStyleIOSCustom) + ? NSDictionaryOfVariableBindings(firstButton, separatorView, lastButton) + : NSDictionaryOfVariableBindings(firstButton, lastButton); + + [self.actionButtonContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:format + options:NSLayoutFormatAlignAllCenterY + metrics:nil + views:views]]; + + format = (self.style == NYAlertViewStyleIOSCustom) + ? @"V:|-0-[firstButton(40)]-0-|" + : @"V:|-[firstButton(40)]|"; + + [self.actionButtonContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:format + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_contentViewContainerView, firstButton)]]; + + [self.actionButtonContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:[lastButton(40)]" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(lastButton)]]; } else { - for (int i = 0; i < [actionButtons count]; i++) { + for (NSUInteger i = 0; i < [actionButtons count]; i++) { UIButton *actionButton = actionButtons[i]; - + [self.actionButtonContainerView addSubview:actionButton]; - - [self.actionButtonContainerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[actionButton]-|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(actionButton)]]; - - [self.actionButtonContainerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[actionButton(40)]" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(actionButton)]]; - + + [self.actionButtonContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:|-[actionButton]-|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(actionButton)]]; + + [self.actionButtonContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:[actionButton(40)]" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(actionButton)]]; + if (i == 0) { - [self.actionButtonContainerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[actionButton]" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(_contentViewContainerView, actionButton)]]; + [self.actionButtonContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:|-[actionButton]" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_contentViewContainerView, actionButton)]]; } else { UIButton *previousButton = actionButtons[i - 1]; - - [self.actionButtonContainerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[previousButton]-[actionButton]" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(previousButton, actionButton)]]; + + [self.actionButtonContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:[previousButton]-[actionButton]" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(previousButton, actionButton)]]; } - + if (i == ([actionButtons count] - 1)) { - [self.actionButtonContainerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[actionButton]|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(actionButton)]]; + [self.actionButtonContainerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:[actionButton]|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(actionButton)]]; } } } } +- (void)adjustVerticalPosition +{ + if (CGSizeEqualToSize(self.keyboardSize, CGSizeZero)) { + return; + } + + UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + + if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight ) { + self.keyboardSize = CGSizeMake(self.keyboardSize.height, self.keyboardSize.width); + } + + CGFloat keyboardTop = [[UIScreen mainScreen] bounds].size.height - self.keyboardSize.height; + CGPoint textFieldPosition = [self convertPoint:CGPointZero fromView:self.activeView]; + CGFloat textFieldBottom = textFieldPosition.y + self.activeView.frame.size.height; + + if (textFieldBottom > keyboardTop) { + _backgroundViewVerticalCenteringConstraint.constant = keyboardTop - textFieldBottom; + [self setNeedsUpdateConstraints]; + + [UIView animateWithDuration:0.2f animations:^{ + [self layoutIfNeeded]; + }]; + } +} + +- (void)updateMessageTextViewHeight +{ + BOOL isEmpty = (self.messageTextView.text.length == 0); + CGFloat height = isEmpty ? 0 : [self.messageTextView sizeThatFits:CGSizeMake(self.messageTextView.intrinsicContentSize.width, CGFLOAT_MAX)].height; + + self.messageTextViewHeightConstraint.constant = 0; + + CGFloat topMargin = 0; + BOOL isTitleEmpty = (self.titleLabel.text.length == 0); + + if (!isEmpty && isTitleEmpty) { + topMargin = 8; + } else if (!isTitleEmpty) { + topMargin = 2; + } + self.messageTextViewTopConstraint.constant = topMargin; +} + +- (void)updateBackgroundViewVerticalCenteringConstraint +{ + self.backgroundViewVerticalCenteringConstraint.constant = self.isFullScreen ? self.statusBarHeight / 2 : 0; +} + +- (UIView *)activeView +{ + return self.keyboardEscapingView ? self.keyboardEscapingView : _activeTextField; +} + +- (CGFloat)statusBarHeight +{ + return UIApplication.sharedApplication.statusBarFrame.size.height; +} + +#pragma mark - Notifications + +- (void)keyboardWillShowNotification:(NSNotification *)notification +{ + if (self.keyboardIsVisible) { + return; + } + + self.keyboardIsVisible = YES; + + // use max keyboard size here because begin/end keys can contain different values + // in different use cases (selecting input or navigation with Next button) + CGSize beginSize = [[notification userInfo][UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; + CGSize endSize = [[notification userInfo][UIKeyboardFrameEndUserInfoKey] CGRectValue].size; + self.keyboardSize = CGSizeMake(beginSize.width, MAX(beginSize.height, endSize.height)); + + [self adjustVerticalPosition]; +} + +- (void)keyboardWillHideNotification:(NSNotification *)notification +{ + self.keyboardIsVisible = NO; + [self updateBackgroundViewVerticalCenteringConstraint]; + [self setNeedsUpdateConstraints]; + + [UIView animateWithDuration:0.2f animations:^{ + [self layoutIfNeeded]; + }]; +} + +- (void)textFieldDidBeginEditingNotification:(NSNotification *)notification +{ + _activeTextField = notification.object; + + // call here for the case if we press Next on keyboard and go to the next (possibly) hidden text field + [self adjustVerticalPosition]; +} + +- (void)textFieldDidEndEditingNotification:(NSNotification *)notification +{ + _activeTextField = nil; +} + @end diff --git a/NYAlertViewController/NYAlertViewController.h b/NYAlertViewController/NYAlertViewController.h index 88fb990..50de157 100644 --- a/NYAlertViewController/NYAlertViewController.h +++ b/NYAlertViewController/NYAlertViewController.h @@ -6,6 +6,7 @@ // #import +#import "NYAlertView.h" @interface NYAlertAction : NSObject @@ -13,7 +14,7 @@ @property (nonatomic) NSString *title; @property (nonatomic) UIAlertActionStyle style; -@property (nonatomic, strong) void (^handler)(NYAlertAction *action); +@property (nonatomic) void (^handler)(NYAlertAction *action); @property (nonatomic) BOOL enabled; @end @@ -34,11 +35,22 @@ typedef NS_ENUM(NSInteger, NYAlertViewControllerTransitionStyle) { */ + (instancetype)alertControllerWithTitle:(NSString *)title message:(NSString *)message; +- (instancetype)initWithAlertViewStyle:(NYAlertViewStyle)style; + +@property (nonatomic) NYAlertView *view; + +@property (nonatomic) NYAlertViewStyle alertViewStyle; + /** The message displayed under the alert view's title */ @property (nonatomic) NSString *message; +/** + The attributed message displayed under the alert view's title + */ +@property (nonatomic) NSAttributedString *attributedMessage; + /** A Boolean value that determines whether the status bar is visible when the alert view is presented */ @@ -77,11 +89,16 @@ typedef NS_ENUM(NSInteger, NYAlertViewControllerTransitionStyle) { */ @property (nonatomic) UIColor *alertViewBackgroundColor; + +@property (nonatomic) UIColor *backgroundDimmingViewBackgroundColor; + /** The maximum width at which to display the presented alert view */ @property (nonatomic) CGFloat maximumWidth; +@property (nonatomic) BOOL isFullScreen; + /** The font used to display the title in the alert view @@ -125,6 +142,36 @@ typedef NS_ENUM(NSInteger, NYAlertViewControllerTransitionStyle) { */ @property (nonatomic) UIColor *messageColor; +/** + The border color for the alert view's buttons corresponsing to default style actions + */ +@property (nonatomic) UIColor *buttonBorderColor; + +/** + The border color for the alert view's buttons corresponsing to cancel style actions + */ +@property (nonatomic) UIColor *cancelButtonBorderColor; + +/** + The border color for the alert view's buttons corresponsing to destructive style actions + */ +@property (nonatomic) UIColor *destructiveButtonBorderColor; + +/** + The border width for the alert view's buttons corresponsing to default style actions + */ +@property (nonatomic) CGFloat buttonBorderWidth; + +/** + The border width for the alert view's buttons corresponsing to cancel style actions + */ +@property (nonatomic) CGFloat cancelButtonBorderWidth; + +/** + The border width for the alert view's buttons corresponsing to cancel style actions + */ +@property (nonatomic) CGFloat destructiveButtonBorderWidth; + /** The background color for the alert view's buttons corresponsing to default style actions */ @@ -178,7 +225,7 @@ typedef NS_ENUM(NSInteger, NYAlertViewControllerTransitionStyle) { /** An array of NYAlertAction objects representing the actions that the user can take in response to the alert view */ -@property (nonatomic, readonly) NSArray *actions; +@property (nonatomic, readonly) NSArray *actions; /** An array of UITextField objects displayed by the alert view @@ -201,4 +248,6 @@ typedef NS_ENUM(NSInteger, NYAlertViewControllerTransitionStyle) { */ - (void)addTextFieldWithConfigurationHandler:(void (^)(UITextField *textField))configurationHandler; +- (void)clearActions; + @end diff --git a/NYAlertViewController/NYAlertViewController.m b/NYAlertViewController/NYAlertViewController.m index 7d3e8ab..7816e69 100644 --- a/NYAlertViewController/NYAlertViewController.m +++ b/NYAlertViewController/NYAlertViewController.m @@ -7,8 +7,6 @@ #import "NYAlertViewController.h" -#import "NYAlertView.h" - @interface NYAlertAction () @property (weak, nonatomic) UIButton *actionButton; @@ -22,23 +20,23 @@ + (instancetype)actionWithTitle:(NSString *)title style:(UIAlertActionStyle)styl action.title = title; action.style = style; action.handler = handler; - + return action; } - (instancetype)init { self = [super init]; - + if (self) { _enabled = YES; } - + return self; } - (void)setEnabled:(BOOL)enabled { _enabled = enabled; - + self.actionButton.enabled = enabled; } @@ -57,35 +55,35 @@ @implementation NYAlertViewPresentationAnimationController - (instancetype)init { self = [super init]; - + if (self) { self.duration = kDefaultPresentationAnimationDuration; } - + return self; } - (void)animateTransition:(id)transitionContext { if (self.transitionStyle == NYAlertViewControllerTransitionStyleSlideFromTop || self.transitionStyle == NYAlertViewControllerTransitionStyleSlideFromBottom) { UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; - + CGRect initialFrame = [transitionContext finalFrameForViewController:toViewController]; - + initialFrame.origin.y = self.transitionStyle == NYAlertViewControllerTransitionStyleSlideFromTop ? -(initialFrame.size.height + initialFrame.origin.y) : (initialFrame.size.height + initialFrame.origin.y); toViewController.view.frame = initialFrame; - + [[transitionContext containerView] addSubview:toViewController.view]; - + // If we're using the slide from top transition, apply a 3D rotation effect to the alert view as it animates in if (self.transitionStyle == NYAlertViewControllerTransitionStyleSlideFromTop) { CATransform3D transform = CATransform3DIdentity; transform.m34 = -1.0f / 600.0f; transform = CATransform3DRotate(transform, M_PI_4 * 1.3f, 1.0f, 0.0f, 0.0f); - + toViewController.view.layer.zPosition = 100.0f; toViewController.view.layer.transform = transform; } - + [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0f usingSpringWithDamping:0.76f @@ -101,13 +99,13 @@ - (void)animateTransition:(id)transitionCo }]; } else { UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; - + toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController]; [[transitionContext containerView] addSubview:toViewController.view]; - + toViewController.view.layer.transform = CATransform3DMakeScale(1.2f, 1.2f, 1.2f); toViewController.view.layer.opacity = 0.0f; - + [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ toViewController.view.layer.transform = CATransform3DIdentity; @@ -123,12 +121,13 @@ - (NSTimeInterval)transitionDuration:(id)t switch (self.transitionStyle) { case NYAlertViewControllerTransitionStyleFade: return 0.3f; - break; - + case NYAlertViewControllerTransitionStyleSlideFromTop: case NYAlertViewControllerTransitionStyleSlideFromBottom: return 0.6f; } + + return 0.0f; } @end @@ -146,21 +145,21 @@ @implementation NYAlertViewDismissalAnimationController - (instancetype)init { self = [super init]; - + if (self) { self.duration = kDefaultDismissalAnimationDuration; } - + return self; } - (void)animateTransition:(id)transitionContext { if (self.transitionStyle == NYAlertViewControllerTransitionStyleSlideFromTop || self.transitionStyle == NYAlertViewControllerTransitionStyleSlideFromBottom) { UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; - + CGRect finalFrame = [transitionContext finalFrameForViewController:fromViewController]; finalFrame.origin.y = 1.2f * CGRectGetHeight([transitionContext containerView].frame); - + [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0f usingSpringWithDamping:0.8f @@ -174,7 +173,7 @@ - (void)animateTransition:(id)transitionCo }]; } else { UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; - + [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ fromViewController.view.layer.opacity = 0.0f; @@ -189,19 +188,19 @@ - (NSTimeInterval)transitionDuration:(id)t switch (self.transitionStyle) { case NYAlertViewControllerTransitionStyleFade: return 0.3f; - break; - + case NYAlertViewControllerTransitionStyleSlideFromTop: case NYAlertViewControllerTransitionStyleSlideFromBottom: return 0.6f; - }} + } + + return 0.0f; +} @end @interface NYAlertViewPresentationController : UIPresentationController -@property CGFloat presentedViewControllerHorizontalInset; -@property CGFloat presentedViewControllerVerticalInset; @property (nonatomic) BOOL backgroundTapDismissalGestureEnabled; @property UIView *backgroundDimmingView; @@ -216,33 +215,37 @@ - (void)tapGestureRecognized:(UITapGestureRecognizer *)gestureRecognizer; @implementation NYAlertViewPresentationController - (void)presentationTransitionWillBegin { - self.presentedViewController.view.layer.cornerRadius = 6.0f; - self.presentedViewController.view.layer.masksToBounds = YES; - + NYAlertViewController *presentedViewController = (NYAlertViewController *) self.presentedViewController; + presentedViewController.view.layer.masksToBounds = YES; + self.backgroundDimmingView = [[UIView alloc] initWithFrame:CGRectZero]; [self.backgroundDimmingView setTranslatesAutoresizingMaskIntoConstraints:NO]; self.backgroundDimmingView.alpha = 0.0f; - self.backgroundDimmingView.backgroundColor = [UIColor blackColor]; + self.backgroundDimmingView.backgroundColor = presentedViewController.backgroundDimmingViewBackgroundColor + ? presentedViewController.backgroundDimmingViewBackgroundColor + : [UIColor blackColor]; [self.containerView addSubview:self.backgroundDimmingView]; - - [self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_backgroundDimmingView]|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(_backgroundDimmingView)]]; - - [self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_backgroundDimmingView]|" - options:0 - metrics:nil - views:NSDictionaryOfVariableBindings(_backgroundDimmingView)]]; - + + [self.containerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"H:|[_backgroundDimmingView]|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_backgroundDimmingView)]]; + + [self.containerView addConstraints:[NSLayoutConstraint + constraintsWithVisualFormat:@"V:|[_backgroundDimmingView]|" + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(_backgroundDimmingView)]]; + UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureRecognized:)]; [self.backgroundDimmingView addGestureRecognizer:tapGestureRecognizer]; - + // Shrink the presenting view controller, and animate in the dark background view id transitionCoordinator = [self.presentingViewController transitionCoordinator]; [transitionCoordinator animateAlongsideTransition:^(id context) { - self.backgroundDimmingView.alpha = 0.7f; - } + self.backgroundDimmingView.alpha = 0.7f; + } completion:nil]; } @@ -256,7 +259,7 @@ - (BOOL)shouldRemovePresentersView { - (void)presentationTransitionDidEnd:(BOOL)completed { [super presentationTransitionDidEnd:completed]; - + if (!completed) { [self.backgroundDimmingView removeFromSuperview]; } @@ -264,26 +267,26 @@ - (void)presentationTransitionDidEnd:(BOOL)completed { - (void)dismissalTransitionWillBegin { [super dismissalTransitionWillBegin]; - + id transitionCoordinator = [self.presentingViewController transitionCoordinator]; [transitionCoordinator animateAlongsideTransition:^(id context) { - self.backgroundDimmingView.alpha = 0.0f; - - self.presentingViewController.view.transform = CGAffineTransformIdentity; - } + self.backgroundDimmingView.alpha = 0.0f; + + self.presentingViewController.view.transform = CGAffineTransformIdentity; + } completion:nil]; } - (void)containerViewWillLayoutSubviews { [super containerViewWillLayoutSubviews]; - + [self presentedView].frame = [self frameOfPresentedViewInContainerView]; self.backgroundDimmingView.frame = self.containerView.bounds; } - (void)dismissalTransitionDidEnd:(BOOL)completed { [super dismissalTransitionDidEnd:completed]; - + if (completed) { [self.backgroundDimmingView removeFromSuperview]; } @@ -291,7 +294,11 @@ - (void)dismissalTransitionDidEnd:(BOOL)completed { - (void)tapGestureRecognized:(UITapGestureRecognizer *)gestureRecognizer { if (self.backgroundTapDismissalGestureEnabled) { - [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; + [self.presentingViewController dismissViewControllerAnimated:YES completion:^{ + // clear actions to break retain cycle in action.handler property + NYAlertViewController *vc = (NYAlertViewController *) self.presentedViewController; + [vc clearActions]; + }]; } } @@ -299,7 +306,6 @@ - (void)tapGestureRecognized:(UITapGestureRecognizer *)gestureRecognizer { @interface NYAlertViewController () -@property NYAlertView *view; @property UIPanGestureRecognizer *panGestureRecognizer; @property (nonatomic, strong) id transitioningDelegate; @@ -311,34 +317,55 @@ @implementation NYAlertViewController @dynamic view; +- (instancetype)initWithAlertViewStyle:(NYAlertViewStyle)style +{ + self = [super init]; // calls initWithNibName:bundle: + + if (self) { + self.alertViewStyle = style; + } + + return self; +} + + (instancetype)alertControllerWithTitle:(NSString *)title message:(NSString *)message { NYAlertViewController *alertController = [[NYAlertViewController alloc] initWithNibName:nil bundle:nil]; alertController.title = title; alertController.message = message; - + return alertController; } - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; - + if (self) { [self commonInit]; } - + return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; - + if (self) { [self commonInit]; } - + return self; } +- (void)viewDidLoad +{ + [super viewDidLoad]; + // call here instead of in commonInit to allow initWithAlertViewStyle: to set style + // before loadView: call + if (self.panGestureRecognizer) { + [self.view addGestureRecognizer:self.panGestureRecognizer]; + } +} + - (void)viewDidDisappear:(BOOL)animated { // Necessary to avoid retain cycle - http://stackoverflow.com/a/21218703/1227862 self.transitioningDelegate = nil; @@ -348,37 +375,46 @@ - (void)viewDidDisappear:(BOOL)animated { - (void)commonInit { _actions = [NSArray array]; _textFields = [NSArray array]; - + _showsStatusBar = YES; - + _buttonTitleFont = [UIFont systemFontOfSize:16.0f]; _cancelButtonTitleFont = [UIFont boldSystemFontOfSize:16.0f]; _destructiveButtonTitleFont = [UIFont systemFontOfSize:16.0f]; - + + _buttonBorderWidth = 0.0; + _cancelButtonBorderWidth = 0.0; + _destructiveButtonBorderWidth = 0.0; + + _buttonBorderColor = [UIColor darkGrayColor]; _buttonColor = [UIColor darkGrayColor]; _buttonTitleColor = [UIColor whiteColor]; + + _cancelButtonBorderColor = [UIColor darkGrayColor]; _cancelButtonColor = [UIColor darkGrayColor]; _cancelButtonTitleColor = [UIColor whiteColor]; + + _destructiveButtonBorderColor = [UIColor colorWithRed:1.0f green:0.23f blue:0.21f alpha:1.0f]; _destructiveButtonColor = [UIColor colorWithRed:1.0f green:0.23f blue:0.21f alpha:1.0f]; _destructiveButtonTitleColor = [UIColor whiteColor]; + _disabledButtonColor = [UIColor lightGrayColor]; _disabledButtonTitleColor = [UIColor whiteColor]; - + _buttonCornerRadius = 6.0f; - + _transitionStyle = NYAlertViewControllerTransitionStyleSlideFromTop; - + self.modalPresentationStyle = UIModalPresentationCustom; self.transitioningDelegate = self; - + self.panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureRecognized:)]; self.panGestureRecognizer.delegate = self; self.panGestureRecognizer.enabled = NO; - [self.view addGestureRecognizer:self.panGestureRecognizer]; } - (void)loadView { - self.view = [[NYAlertView alloc] initWithFrame:CGRectZero]; + self.view = [[NYAlertView alloc] initWithStyle:self.alertViewStyle]; } - (BOOL)prefersStatusBarHidden { @@ -393,6 +429,12 @@ - (void)setMaximumWidth:(CGFloat)maximumWidth { self.view.maximumWidth = maximumWidth; } +- (void)setIsFullScreen:(BOOL)isFullScreen +{ + _isFullScreen = isFullScreen; + self.view.isFullScreen = isFullScreen; +} + - (UIView *)alertViewContentView { return self.view.contentView; } @@ -403,33 +445,33 @@ - (void)setAlertViewContentView:(UIView *)alertViewContentView { - (void)setSwipeDismissalGestureEnabled:(BOOL)swipeDismissalGestureEnabled { _swipeDismissalGestureEnabled = swipeDismissalGestureEnabled; - + self.panGestureRecognizer.enabled = swipeDismissalGestureEnabled; } - (void)panGestureRecognized:(UIPanGestureRecognizer *)gestureRecognizer { self.view.backgroundViewVerticalCenteringConstraint.constant = [gestureRecognizer translationInView:self.view].y; - + NYAlertViewPresentationController *presentationController = (NYAlertViewPresentationController* )self.presentationController; - - CGFloat windowHeight = CGRectGetHeight([UIApplication sharedApplication].keyWindow.bounds); + + CGFloat windowHeight = CGRectGetHeight(UIScreen.mainScreen.bounds); presentationController.backgroundDimmingView.alpha = 0.7f - (fabs([gestureRecognizer translationInView:self.view].y) / windowHeight); - + if (gestureRecognizer.state == UIGestureRecognizerStateEnded) { CGFloat verticalGestureVelocity = [gestureRecognizer velocityInView:self.view].y; - + // If the gesture is moving fast enough, animate the alert view offscreen and dismiss the view controller. Otherwise, animate the alert view back to its initial position if (fabs(verticalGestureVelocity) > 500.0f) { CGFloat backgroundViewYPosition; - + if (verticalGestureVelocity > 500.0f) { backgroundViewYPosition = CGRectGetHeight(self.view.frame); } else { backgroundViewYPosition = -CGRectGetHeight(self.view.frame); } - + CGFloat animationDuration = 500.0f / fabs(verticalGestureVelocity); - + self.view.backgroundViewVerticalCenteringConstraint.constant = backgroundViewYPosition; [UIView animateWithDuration:animationDuration delay:0.0f @@ -441,7 +483,10 @@ - (void)panGestureRecognized:(UIPanGestureRecognizer *)gestureRecognizer { [self.view layoutIfNeeded]; } completion:^(BOOL finished) { - [self dismissViewControllerAnimated:YES completion:nil]; + [self dismissViewControllerAnimated:YES completion:^{ + // clear actions to break retain cycle in action.handler property + [self clearActions]; + }]; }]; } else { self.view.backgroundViewVerticalCenteringConstraint.constant = 0.0f; @@ -461,63 +506,83 @@ - (void)panGestureRecognized:(UIPanGestureRecognizer *)gestureRecognizer { - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - + [self createActionButtons]; self.view.textFields = self.textFields; } +- (UIColor *)alertViewBackgroundColor { + return self.view.alertBackgroundView.backgroundColor; +} + +- (void)setBackgroundDimmingViewBackgroundColor:(UIColor *)backgroundDimmingViewBackgroundColor +{ + _backgroundDimmingViewBackgroundColor = backgroundDimmingViewBackgroundColor; +} + - (void)setAlertViewBackgroundColor:(UIColor *)alertViewBackgroundColor { - _alertViewBackgroundColor = alertViewBackgroundColor; - self.view.alertBackgroundView.backgroundColor = alertViewBackgroundColor; } - (void)createActionButtons { NSMutableArray *buttons = [NSMutableArray array]; - + // Create buttons for each action for (int i = 0; i < [self.actions count]; i++) { NYAlertAction *action = self.actions[i]; - + NYAlertViewButton *button = [NYAlertViewButton buttonWithType:UIButtonTypeCustom]; - + button.tag = i; [button addTarget:self action:@selector(actionButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; - + button.enabled = action.enabled; - - button.cornerRadius = self.buttonCornerRadius; + + button.cornerRadius = (self.alertViewStyle == NYAlertViewStyleIOSCustom) ? 0 : self.buttonCornerRadius; [button setTranslatesAutoresizingMaskIntoConstraints:NO]; [button setTitle:action.title forState:UIControlStateNormal]; - + [button setTitleColor:self.disabledButtonTitleColor forState:UIControlStateDisabled]; [button setBackgroundColor:self.disabledButtonColor forState:UIControlStateDisabled]; - + if (action.style == UIAlertActionStyleCancel) { + button.type = (_cancelButtonBorderWidth > 0) ? NYAlertViewButtonTypeBordered : NYAlertViewButtonTypeFilled; + [button setBorderWidth:_cancelButtonBorderWidth]; + [button setBorderColor:_cancelButtonBorderColor]; + [button setBorderedTitleColor:self.cancelButtonTitleColor]; [button setTitleColor:self.cancelButtonTitleColor forState:UIControlStateNormal]; [button setTitleColor:self.cancelButtonTitleColor forState:UIControlStateHighlighted]; [button setBackgroundColor:self.cancelButtonColor forState:UIControlStateNormal]; button.titleLabel.font = self.cancelButtonTitleFont; } else if (action.style == UIAlertActionStyleDestructive) { + button.type = (_destructiveButtonBorderWidth > 0) ? NYAlertViewButtonTypeBordered : NYAlertViewButtonTypeFilled; + [button setBorderWidth:_destructiveButtonBorderWidth]; + [button setBorderColor:_destructiveButtonBorderColor]; + [button setBorderedTitleColor:self.destructiveButtonTitleColor]; [button setTitleColor:self.destructiveButtonTitleColor forState:UIControlStateNormal]; [button setTitleColor:self.destructiveButtonTitleColor forState:UIControlStateHighlighted]; [button setBackgroundColor:self.destructiveButtonColor forState:UIControlStateNormal]; button.titleLabel.font = self.destructiveButtonTitleFont; } else { + button.type = (_buttonBorderWidth > 0) ? NYAlertViewButtonTypeBordered : NYAlertViewButtonTypeFilled; + [button setBorderWidth:_buttonBorderWidth]; + [button setBorderColor:_buttonBorderColor]; + [button setBorderedTitleColor:self.buttonTitleColor]; [button setTitleColor:self.buttonTitleColor forState:UIControlStateNormal]; [button setTitleColor:self.buttonTitleColor forState:UIControlStateHighlighted]; [button setBackgroundColor:self.buttonColor forState:UIControlStateNormal]; button.titleLabel.font = self.buttonTitleFont; } - + [buttons addObject:button]; - + + button.enabled = action.enabled; action.actionButton = button; } - + self.view.actionButtons = buttons; } @@ -530,13 +595,19 @@ - (void)actionButtonPressed:(UIButton *)button { - (void)setTitle:(NSString *)title { [super setTitle:title]; - - self.view.titleLabel.text = title; + self.view.title = title; } - (void)setMessage:(NSString *)message { _message = message; self.view.messageTextView.text = message; + [self.view updateMessageTextViewHeight]; +} + +- (void)setAttributedMessage:(NSAttributedString *)attributedMessage { + _attributedMessage = attributedMessage; + self.view.messageTextView.attributedText = attributedMessage; + [self.view updateMessageTextViewHeight]; } - (UIFont *)titleFont { @@ -557,10 +628,10 @@ - (void)setMessageFont:(UIFont *)messageFont { - (void)setButtonTitleFont:(UIFont *)buttonTitleFont { _buttonTitleFont = buttonTitleFont; - + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { NYAlertAction *action = self.actions[idx]; - + if (action.style != UIAlertActionStyleCancel) { button.titleLabel.font = buttonTitleFont; } @@ -569,10 +640,10 @@ - (void)setButtonTitleFont:(UIFont *)buttonTitleFont { - (void)setCancelButtonTitleFont:(UIFont *)cancelButtonTitleFont { _cancelButtonTitleFont = cancelButtonTitleFont; - + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { NYAlertAction *action = self.actions[idx]; - + if (action.style == UIAlertActionStyleCancel) { button.titleLabel.font = cancelButtonTitleFont; } @@ -581,10 +652,10 @@ - (void)setCancelButtonTitleFont:(UIFont *)cancelButtonTitleFont { - (void)setDestructiveButtonTitleFont:(UIFont *)destructiveButtonTitleFont { _destructiveButtonTitleFont = destructiveButtonTitleFont; - + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { NYAlertAction *action = self.actions[idx]; - + if (action.style == UIAlertActionStyleDestructive) { button.titleLabel.font = destructiveButtonTitleFont; } @@ -607,12 +678,84 @@ - (void)setMessageColor:(UIColor *)messageColor { self.view.messageTextView.textColor = messageColor; } +- (void)setButtonBorderColor:(UIColor *)buttonBorderColor { + _buttonBorderColor = buttonBorderColor; + + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { + NYAlertAction *action = self.actions[idx]; + + if (action.style != UIAlertActionStyleCancel) { + [button.layer setBorderColor:buttonBorderColor.CGColor]; + } + }]; +} + +- (void)setCancelButtonBorderColor:(UIColor *)cancelButtonBorderColor { + _cancelButtonBorderColor = cancelButtonBorderColor; + + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { + NYAlertAction *action = self.actions[idx]; + + if (action.style == UIAlertActionStyleCancel) { + [button.layer setBorderColor:cancelButtonBorderColor.CGColor]; + } + }]; +} + +- (void)setDestructiveButtonBorderColor:(UIColor *)destructiveButtonBorderColor { + _destructiveButtonBorderColor = destructiveButtonBorderColor; + + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { + NYAlertAction *action = self.actions[idx]; + + if (action.style == UIAlertActionStyleDestructive) { + [button.layer setBorderColor:destructiveButtonBorderColor.CGColor]; + } + }]; +} + +- (void)setButtonBorderWidth:(CGFloat)buttonBorderWidth { + _buttonBorderWidth = buttonBorderWidth; + + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { + NYAlertAction *action = self.actions[idx]; + + if (action.style != UIAlertActionStyleCancel) { + [button.layer setBorderWidth:buttonBorderWidth]; + } + }]; +} + +- (void)setCancelButtonBorderWidth:(CGFloat)cancelButtonBorderWidth { + _cancelButtonBorderWidth = cancelButtonBorderWidth; + + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { + NYAlertAction *action = self.actions[idx]; + + if (action.style == UIAlertActionStyleCancel) { + [button.layer setBorderWidth:cancelButtonBorderWidth]; + } + }]; +} + +- (void)setDestructiveButtonBorderWidth:(CGFloat)destructiveButtonBorderWidth { + _destructiveButtonBorderWidth = destructiveButtonBorderWidth; + + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { + NYAlertAction *action = self.actions[idx]; + + if (action.style == UIAlertActionStyleDestructive) { + [button.layer setBorderWidth:destructiveButtonBorderWidth]; + } + }]; +} + - (void)setButtonColor:(UIColor *)buttonColor { _buttonColor = buttonColor; - + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { NYAlertAction *action = self.actions[idx]; - + if (action.style != UIAlertActionStyleCancel) { [button setBackgroundColor:buttonColor forState:UIControlStateNormal]; } @@ -621,10 +764,10 @@ - (void)setButtonColor:(UIColor *)buttonColor { - (void)setCancelButtonColor:(UIColor *)cancelButtonColor { _cancelButtonColor = cancelButtonColor; - + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { NYAlertAction *action = self.actions[idx]; - + if (action.style == UIAlertActionStyleCancel) { [button setBackgroundColor:cancelButtonColor forState:UIControlStateNormal]; } @@ -633,10 +776,10 @@ - (void)setCancelButtonColor:(UIColor *)cancelButtonColor { - (void)setDestructiveButtonColor:(UIColor *)destructiveButtonColor { _destructiveButtonColor = destructiveButtonColor; - + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { NYAlertAction *action = self.actions[idx]; - + if (action.style == UIAlertActionStyleDestructive) { [button setBackgroundColor:destructiveButtonColor forState:UIControlStateNormal]; } @@ -645,10 +788,10 @@ - (void)setDestructiveButtonColor:(UIColor *)destructiveButtonColor { - (void)setDisabledButtonColor:(UIColor *)disabledButtonColor { _disabledButtonColor = disabledButtonColor; - + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { NYAlertAction *action = self.actions[idx]; - + if (!action.enabled) { [button setBackgroundColor:disabledButtonColor forState:UIControlStateNormal]; } @@ -657,10 +800,10 @@ - (void)setDisabledButtonColor:(UIColor *)disabledButtonColor { - (void)setButtonTitleColor:(UIColor *)buttonTitleColor { _buttonTitleColor = buttonTitleColor; - + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { NYAlertAction *action = self.actions[idx]; - + if (action.style != UIAlertActionStyleCancel) { [button setTitleColor:buttonTitleColor forState:UIControlStateNormal]; [button setTitleColor:buttonTitleColor forState:UIControlStateHighlighted]; @@ -670,10 +813,10 @@ - (void)setButtonTitleColor:(UIColor *)buttonTitleColor { - (void)setCancelButtonTitleColor:(UIColor *)cancelButtonTitleColor { _cancelButtonTitleColor = cancelButtonTitleColor; - + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { NYAlertAction *action = self.actions[idx]; - + if (action.style == UIAlertActionStyleCancel) { [button setTitleColor:cancelButtonTitleColor forState:UIControlStateNormal]; [button setTitleColor:cancelButtonTitleColor forState:UIControlStateHighlighted]; @@ -683,10 +826,10 @@ - (void)setCancelButtonTitleColor:(UIColor *)cancelButtonTitleColor { - (void)setDestructiveButtonTitleColor:(UIColor *)destructiveButtonTitleColor { _destructiveButtonTitleColor = destructiveButtonTitleColor; - + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { NYAlertAction *action = self.actions[idx]; - + if (action.style == UIAlertActionStyleDestructive) { [button setTitleColor:destructiveButtonTitleColor forState:UIControlStateNormal]; [button setTitleColor:destructiveButtonTitleColor forState:UIControlStateHighlighted]; @@ -696,10 +839,10 @@ - (void)setDestructiveButtonTitleColor:(UIColor *)destructiveButtonTitleColor { - (void)setDisabledButtonTitleColor:(UIColor *)disabledButtonTitleColor { _disabledButtonTitleColor = disabledButtonTitleColor; - + [self.view.actionButtons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL *stop) { NYAlertAction *action = self.actions[idx]; - + if (!action.enabled) { [button setTitleColor:disabledButtonTitleColor forState:UIControlStateNormal]; [button setTitleColor:disabledButtonTitleColor forState:UIControlStateHighlighted]; @@ -717,7 +860,7 @@ - (void)setAlertViewCornerRadius:(CGFloat)alertViewCornerRadius { - (void)setButtonCornerRadius:(CGFloat)buttonCornerRadius { _buttonCornerRadius = buttonCornerRadius; - + for (NYAlertViewButton *button in self.view.actionButtons) { button.cornerRadius = buttonCornerRadius; } @@ -730,12 +873,17 @@ - (void)addAction:(UIAlertAction *)action { - (void)addTextFieldWithConfigurationHandler:(void (^)(UITextField *textField))configurationHandler { UITextField *textField = [[UITextField alloc] initWithFrame:CGRectZero]; textField.borderStyle = UITextBorderStyleRoundedRect; - + configurationHandler(textField); - + _textFields = [self.textFields arrayByAddingObject:textField]; } +- (void)clearActions +{ + _actions = [NSArray new]; +} + - (void)buttonPressed:(UIButton *)sender { NYAlertAction *action = self.actions[sender.tag]; action.handler(action); @@ -773,7 +921,7 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceive if (([touch.view isKindOfClass:[UIButton class]])) { return NO; } - + return YES; } diff --git a/NYAlertViewControllerDemo/NYAlertViewDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/NYAlertViewControllerDemo/NYAlertViewDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/NYAlertViewControllerDemo/NYAlertViewDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/NYAlertViewControllerDemo/NYAlertViewDemo/Base.lproj/LaunchScreen.xib b/NYAlertViewControllerDemo/NYAlertViewDemo/Base.lproj/LaunchScreen.xib index 675002e..8b02834 100644 --- a/NYAlertViewControllerDemo/NYAlertViewDemo/Base.lproj/LaunchScreen.xib +++ b/NYAlertViewControllerDemo/NYAlertViewDemo/Base.lproj/LaunchScreen.xib @@ -8,17 +8,17 @@ - +