SPAlertController.m 91 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683
  1. //
  2. // SPAlertController.m
  3. // SPAlertController
  4. //
  5. // Created by 乐升平 on 17/10/12. https://github.com/SPStore/SPAlertController
  6. // Copyright © 2017年 iDress. All rights reserved.
  7. //
  8. #import "SPAlertController.h"
  9. #define ScreenWidth [UIScreen mainScreen].bounds.size.width
  10. #define ScreenHeight [UIScreen mainScreen].bounds.size.height
  11. #define actionHeight 48.0
  12. #define actionColor [UIColor colorWithWhite:1 alpha:0.8]
  13. #define alertColor [UIColor colorWithWhite:1 alpha:0]
  14. #define isIPhoneX ([UIScreen mainScreen].bounds.size.height==812)
  15. #define alertBottomMargin isIPhoneX ? 34 : 0 // 适配iPhoneX
  16. #pragma mark ---------------------------- SPAlertAction begin --------------------------------
  17. @interface SPAlertAction()
  18. @property (nullable, nonatomic) NSString *title;
  19. @property (nonatomic, assign) SPAlertActionStyle style;
  20. @property (nonatomic, copy) void (^handler)(SPAlertAction *action);
  21. // 当在addAction之后设置action属性时,会回调这个block,设置相应控件的字体、颜色等
  22. // 如果没有这个block,那使用时,只有在addAction之前设置action的属性才有效
  23. @property (nonatomic, copy) void (^propertyEvent)(SPAlertAction *action);
  24. @end
  25. @implementation SPAlertAction
  26. - (id)copyWithZone:(NSZone *)zone {
  27. SPAlertAction *action = [[[self class] alloc] init];
  28. action.title = [self.title copy];
  29. action.style = self.style;
  30. action.enabled = self.enabled;
  31. action.titleColor = self.titleColor;
  32. action.titleFont = self.titleFont;
  33. action.handler = self.handler;
  34. return action;
  35. }
  36. + (instancetype)actionWithTitle:(nullable NSString *)title style:(SPAlertActionStyle)style handler:(void (^ __nullable)(SPAlertAction *action))handler {
  37. SPAlertAction *action = [[self alloc] initWithTitle:title style:(SPAlertActionStyle)style handler:handler];
  38. return action;
  39. }
  40. - (instancetype)initWithTitle:(nullable NSString *)title style:(SPAlertActionStyle)style handler:(void (^ __nullable)(SPAlertAction *action))handler {
  41. if (self = [super init]) {
  42. self.title = title;
  43. self.style = style;
  44. self.enabled = YES; // 默认能点击
  45. self.handler = handler;
  46. }
  47. return self;
  48. }
  49. - (void)setTitleColor:(UIColor *)titleColor {
  50. _titleColor = titleColor;
  51. if (self.propertyEvent) {
  52. self.propertyEvent(self);
  53. }
  54. }
  55. - (void)setTitleFont:(UIFont *)titleFont {
  56. _titleFont = titleFont;
  57. if (self.propertyEvent) {
  58. self.propertyEvent(self);
  59. }
  60. }
  61. - (void)setEnabled:(BOOL)enabled {
  62. _enabled = enabled;
  63. if (self.propertyEvent) {
  64. self.propertyEvent(self);
  65. }
  66. }
  67. @end
  68. #pragma mark ---------------------------- SPAlertAction end --------------------------------
  69. #pragma mark ---------------------------- SPAlertControllerActionCell begin --------------------------------
  70. @interface SPAlertControllerActionCell : UITableViewCell
  71. @property (nonatomic, strong) SPAlertAction *action;
  72. @property (nonatomic, weak) UILabel *titleLabel;
  73. @property (nonatomic, strong) NSMutableArray *titleLabelConstraints;
  74. @end
  75. @implementation SPAlertControllerActionCell
  76. - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  77. if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
  78. UILabel *titleLabel = [[UILabel alloc] init];
  79. titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
  80. // 设置垂直方向的抗压缩优先级,优先级越高越不容易被压缩,默认的优先级是750
  81. [titleLabel setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical];
  82. titleLabel.textAlignment = NSTextAlignmentCenter;
  83. titleLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
  84. [titleLabel sizeToFit];
  85. // footerCell指的是SPAlertControllerStyleActionSheet下的取消cell和SPAlertControllerStyleAlert下actions小于_maxNumberOfActionHorizontalArrangementForAlert时的cell
  86. // 这个cell因为要修改系统自带的布局,如果直接加在contentView上,修改contentView的布局很容易出问题,所以此时用不着contentView,而且这个cell跟tableView没有任何关系,就是一个普通的view
  87. if ([reuseIdentifier isEqualToString:@"footerCell"]) {
  88. self.backgroundColor = actionColor;
  89. // contentView在此处没用了
  90. self.contentView.backgroundColor = [UIColor clearColor];
  91. [self addSubview:titleLabel];
  92. } else {
  93. // 设置背景透明,默认是白色,白色会阻挡分割线
  94. self.backgroundColor = [UIColor clearColor];
  95. // contentView默认是透明色,给一个颜色,更加突出分割线(contentView的高度比cell小0.5,0.5就是分割线的高度)
  96. self.contentView.backgroundColor = actionColor;
  97. [self.contentView addSubview:titleLabel];
  98. }
  99. _titleLabel = titleLabel;
  100. [self setNeedsUpdateConstraints];
  101. }
  102. return self;
  103. }
  104. - (void)setAction:(SPAlertAction *)action {
  105. _action = action;
  106. self.titleLabel.text = action.title;
  107. if (action.enabled) {
  108. self.titleLabel.textColor = action.titleColor;
  109. self.titleLabel.font = action.titleFont;
  110. } else {
  111. self.titleLabel.textColor = [UIColor lightGrayColor];
  112. self.titleLabel.font = [UIFont systemFontOfSize:17];
  113. }
  114. self.userInteractionEnabled = action.enabled;
  115. [self setNeedsUpdateConstraints];
  116. }
  117. - (void)updateConstraints {
  118. [super updateConstraints];
  119. UILabel *titleLabel = self.titleLabel;
  120. NSMutableArray *titleLabelConstraints = [NSMutableArray array];
  121. if (self.titleLabelConstraints) {
  122. [titleLabel.superview removeConstraints:self.titleLabelConstraints];
  123. self.titleLabelConstraints = nil;
  124. }
  125. [titleLabelConstraints addObject:[NSLayoutConstraint constraintWithItem:titleLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:titleLabel.superview attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
  126. [titleLabelConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"H:|-(>=0)-[titleLabel]-(>=0)-|"] options:0 metrics:nil views:NSDictionaryOfVariableBindings(titleLabel)]];
  127. [titleLabelConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:|-0-[titleLabel]-0-|"] options:0 metrics:nil views:NSDictionaryOfVariableBindings(titleLabel)]];
  128. [titleLabel.superview addConstraints:titleLabelConstraints];
  129. self.titleLabelConstraints = titleLabelConstraints;
  130. }
  131. + (BOOL)requiresConstraintBasedLayout {
  132. return YES;
  133. }
  134. @end
  135. #pragma mark ---------------------------- SPAlertControllerActionCell end --------------------------------
  136. #pragma mark ---------------------------- SPAlertController begin --------------------------------
  137. @interface SPAlertController () <UIViewControllerTransitioningDelegate,UITableViewDataSource,UITableViewDelegate>
  138. @property (nonatomic, strong) UIView *alertView;
  139. @property (nonatomic, weak) UIVisualEffectView *alertEffectView;
  140. // ---------------- 头部控件 ---------------
  141. @property (nonatomic, weak) UIView *headerBezelView;
  142. @property (nonatomic, weak) UIScrollView *headerScrollView;
  143. @property (nonatomic, weak) UIView *headerScrollContentView; // autoLayout中需要在scrollView上再加一个view
  144. @property (nonatomic, weak) UIView *titleView;
  145. @property (nonatomic, strong) UILabel *titleLabel;
  146. @property (nonatomic, strong) UILabel *detailTitleLabel;
  147. @property (nonatomic, weak) UIView *textFieldView;
  148. // ---------------- 头部控件的约束数组 -----------------
  149. @property (nonatomic, strong) NSMutableArray *headerBezelViewConstraints;
  150. @property (nonatomic, strong) NSMutableArray *headerScrollContentViewConstraints;
  151. @property (nonatomic, strong) NSMutableArray *titleViewConstraints;
  152. @property (nonatomic, strong) NSMutableArray *titleLabelConstraints;
  153. @property (nonatomic, strong) NSMutableArray *textFieldViewConstraints;
  154. @property (nonatomic, strong) NSMutableArray *textFieldConstraints;
  155. // ---------------- action控件 --------------
  156. @property (nonatomic, weak) UIView *actionBezelView;
  157. @property (nonatomic, weak) UIView *actionBezelContentView;
  158. @property (nonatomic, weak) UITableView *actionTableView;
  159. @property (nonatomic, weak) UIView *footerView;
  160. // ---------------- action控件的约束数组 -------------------
  161. @property (nonatomic, strong) NSMutableArray *actionBezelViewConstraints;
  162. @property (nonatomic, strong) NSMutableArray *actionBezelContentViewConstraints;
  163. @property (nonatomic, strong) NSMutableArray *footerViewConstraints;
  164. @property (nonatomic, strong) NSMutableArray *footerActionViewConstraints;
  165. // ---------------- 自定义view --------------
  166. @property (nonatomic, strong) UIView *customView;
  167. @property (nonatomic, strong) UIView *customTitleView;
  168. @property (nonatomic, strong) UIView *customCenterView;
  169. @property (nonatomic, strong) UIView *customFooterView;
  170. @property (nonatomic, assign) CGSize customViewSize;
  171. @property (nonatomic, assign) CGSize customTitleViewSize;
  172. @property (nonatomic, assign) CGSize customCenterViewSize;
  173. @property (nonatomic, assign) CGSize customFooterViewSize;
  174. // ---------------- action控件的约束数组 -------------------
  175. @property (nonatomic, strong) NSMutableArray *customViewConstraints;
  176. // action数组
  177. @property (nonatomic) NSArray<SPAlertAction *> *actions;
  178. // tableView的数据源,与actions数组息息相关
  179. @property (nonatomic, strong) NSArray *dataSource;
  180. // 取消的action
  181. @property (nonatomic, strong) SPAlertAction *cancelAction;
  182. // textFiled数组
  183. @property (nonatomic) NSArray<UITextField *> *textFields;
  184. @property (nonatomic, assign) SPAlertControllerStyle preferredStyle;
  185. @property (nonatomic, assign) SPAlertAnimationType animationType;
  186. @property (nonatomic, assign) BOOL keyboardShow;
  187. @property (nonatomic, assign) NSLayoutConstraint *alertConstraintCenterY;
  188. @end
  189. @implementation SPAlertController
  190. @synthesize title = _title;
  191. - (void)viewDidLoad {
  192. [super viewDidLoad];
  193. // Do any additional setup after loading the view.
  194. }
  195. - (void)dealloc {
  196. [[NSNotificationCenter defaultCenter] removeObserver:self];
  197. }
  198. #pragma mark - Public
  199. + (instancetype)alertControllerWithTitle:(nullable NSString *)title message:(nullable NSString *)message preferredStyle:(SPAlertControllerStyle)preferredStyle animationType:(SPAlertAnimationType)animationType {
  200. SPAlertController *alertController = [self alertControllerWithTitle:title message:message preferredStyle:preferredStyle animationType:animationType customView:nil];
  201. return alertController;
  202. }
  203. + (instancetype)alertControllerWithTitle:(nullable NSString *)title message:(nullable NSString *)message preferredStyle:(SPAlertControllerStyle)preferredStyle animationType:(SPAlertAnimationType)animationType customView:(UIView *)customView {
  204. // 创建控制器
  205. SPAlertController *alertController = [[SPAlertController alloc] initWithTitle:title message:message preferredStyle:preferredStyle animationType:animationType customView:customView customTitleView:nil customCenterView:nil customFooterView:nil];
  206. return alertController;
  207. }
  208. + (instancetype)alertControllerWithPreferredStyle:(SPAlertControllerStyle)preferredStyle animationType:(SPAlertAnimationType)animationType customTitleView:(nullable UIView *)customTitleView {
  209. // 创建控制器
  210. SPAlertController *alertController = [[SPAlertController alloc] initWithTitle:nil message:nil preferredStyle:preferredStyle animationType:animationType customView:nil customTitleView:customTitleView customCenterView:nil customFooterView:nil];
  211. return alertController;
  212. }
  213. + (instancetype)alertControllerWithTitle:(NSString *)title message:(NSString *)message preferredStyle:(SPAlertControllerStyle)preferredStyle animationType:(SPAlertAnimationType)animationType customCenterView:(UIView *)customCenterView {
  214. // 创建控制器
  215. SPAlertController *alertController = [[SPAlertController alloc] initWithTitle:title message:message preferredStyle:preferredStyle animationType:animationType customView:nil customTitleView:nil customCenterView:customCenterView customFooterView:nil];
  216. return alertController;
  217. }
  218. + (instancetype)alertControllerWithTitle:(NSString *)title message:(NSString *)message preferredStyle:(SPAlertControllerStyle)preferredStyle animationType:(SPAlertAnimationType)animationType customFooterView:(UIView *)customFooterView {
  219. // 创建控制器
  220. SPAlertController *alertController = [[SPAlertController alloc] initWithTitle:title message:message preferredStyle:preferredStyle animationType:animationType customView:nil customTitleView:nil customCenterView:nil customFooterView:customFooterView];
  221. return alertController;
  222. }
  223. - (void)addAction:(SPAlertAction *)action {
  224. if ([self.actions containsObject:action]) {
  225. return;
  226. }
  227. NSMutableArray *array = self.actions.mutableCopy;
  228. if (self.cancelAction && self.preferredStyle == SPAlertControllerStyleActionSheet) {
  229. // 如果在取消按钮已经存在的情况下,action又是取消按钮,直接报异常
  230. NSAssert(action.style != SPAlertActionStyleCancel, @"SPAlertController不允许多个取消按钮,你可以检查是否多次设置了SPAlertActionStyleCancel");
  231. NSInteger index = [self.actions indexOfObject:self.cancelAction];
  232. // 将这个action插入到cancelAction之前,目的是保证cancelAction永远处于数组最后一个位置
  233. [array insertObject:action atIndex:index];
  234. } else {
  235. [array addObject:action];
  236. }
  237. self.actions = array;
  238. if (self.preferredStyle == SPAlertControllerStyleActionSheet) {
  239. if (self.customCenterView && action.style != SPAlertActionStyleCancel) {
  240. NSLog(@"当自定义centerView时,SPAlertControllerStyleActionSheet下,除了取消样式的按钮之外,其余样式的按钮均不显示");
  241. [array removeObject:action];
  242. self.actions = array;
  243. [self layoutViewConstraints];
  244. return;
  245. }
  246. if (action.style == SPAlertActionStyleCancel && !self.customFooterView) {
  247. self.cancelAction = action;
  248. [self createFooterActionView:action];
  249. }
  250. self.dataSource = self.cancelAction ? [array subarrayWithRange:NSMakeRange(0, array.count-1)].copy : array.copy;
  251. } else {
  252. if (self.actions.count <= _maxNumberOfActionHorizontalArrangementForAlert && !self.customFooterView) {
  253. [self createFooterActionView:action];
  254. // 当只有_maxNumberOfActionHorizontalArrangementForAlert个action时,不需要tableView,这里没有移除tableView,而是清空数据源,如果直接移除tableView,当大于_maxNumberOfActionHorizontalArrangementForAlert个action时又得加回来
  255. self.dataSource = nil;
  256. } else {
  257. if (self.customCenterView) {
  258. NSLog(@"当自定义centerView时,SPAlertControllerStyleAlert下,action的个数最多只能是_maxNumberOfActionHorizontalArrangementForAlert个,超过_maxNumberOfActionHorizontalArrangementForAlert个的action将不显示");
  259. [array removeObject:action];
  260. self.actions = array;
  261. return;
  262. }
  263. self.dataSource = array.copy;
  264. for (NSInteger i = self.footerView.subviews.count-1; i >= 0; i--) {
  265. UIView *footerActionView = self.footerView.subviews[i];
  266. [footerActionView removeFromSuperview];
  267. footerActionView = nil;
  268. }
  269. }
  270. }
  271. __weak typeof(self) weakSelf = self;
  272. // 当外面在addAction之后再设置action的属性时,会回调这个block
  273. action.propertyEvent = ^(SPAlertAction *action) {
  274. if (weakSelf.preferredStyle == SPAlertControllerStyleActionSheet) {
  275. if (action.style == SPAlertActionStyleCancel) {
  276. // 注意这个cell是与tableView没有任何瓜葛的
  277. SPAlertControllerActionCell *footerCell = weakSelf.footerView.subviews.lastObject;
  278. footerCell.action = action;
  279. } else {
  280. // 刷新tableView
  281. [weakSelf.actionTableView reloadData];
  282. }
  283. } else {
  284. if (weakSelf.actions.count <= _maxNumberOfActionHorizontalArrangementForAlert) {
  285. NSInteger index = [weakSelf.actions indexOfObject:action];
  286. SPAlertControllerActionCell *footerCell = weakSelf.footerView.subviews[index];
  287. footerCell.action = action;
  288. } else {
  289. [weakSelf.actionTableView reloadData];
  290. }
  291. }
  292. };
  293. // 刷新tableView
  294. [self.actionTableView reloadData];
  295. [self layoutViewConstraints];
  296. }
  297. - (void)addTextFieldWithConfigurationHandler:(void (^)(UITextField * _Nonnull))configurationHandler {
  298. UITextField *textField = [[UITextField alloc] init];
  299. textField.translatesAutoresizingMaskIntoConstraints = NO;
  300. // 系统的UITextBorderStyleLine样式线条过于黑,所以自己设置
  301. textField.layer.borderWidth = 0.5;
  302. textField.layer.borderColor = [UIColor grayColor].CGColor;
  303. // 在左边设置一张view,充当光标左边的间距,否则光标紧贴textField不美观
  304. textField.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, 0)];
  305. textField.leftView.userInteractionEnabled = NO;
  306. textField.leftViewMode = UITextFieldViewModeAlways;
  307. textField.font = [UIFont systemFontOfSize:14];
  308. NSMutableArray *array = self.textFields.mutableCopy;
  309. [array addObject:textField];
  310. self.textFields = array;
  311. if (configurationHandler) {
  312. configurationHandler(textField);
  313. }
  314. }
  315. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  316. return self.dataSource.count;
  317. }
  318. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  319. SPAlertControllerActionCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([SPAlertControllerActionCell class]) forIndexPath:indexPath];
  320. SPAlertAction *action = self.dataSource[indexPath.row];
  321. cell.action = action;
  322. return cell;
  323. }
  324. #pragma mark - TableView Method
  325. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  326. return actionHeight;
  327. }
  328. - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
  329. // 设置cell分割线整宽
  330. // Remove seperator inset
  331. if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
  332. [cell setSeparatorInset:UIEdgeInsetsZero];
  333. }
  334. // Prevent the cell from inheriting the Table View's margin settings
  335. if ([cell respondsToSelector:@selector(setPreservesSuperviewLayoutMargins:)]) {
  336. [cell setPreservesSuperviewLayoutMargins:NO];
  337. }
  338. // Explictly set your cell's layout margins
  339. if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
  340. [cell setLayoutMargins:UIEdgeInsetsZero];
  341. }
  342. }
  343. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  344. // 动画置为NO,如果动画为YES,当点击cell退出控制器时会有延迟,延迟时长时短
  345. [tableView deselectRowAtIndexPath:indexPath animated:NO];
  346. SPAlertAction *action = self.dataSource[indexPath.row];
  347. // 回调action的block
  348. if (action.handler) {
  349. action.handler(action);
  350. }
  351. [self dismissViewControllerAnimated:YES completion:nil];
  352. }
  353. #pragma mark - Private
  354. - (instancetype)initWithTitle:(nullable NSString *)title message:(nullable NSString *)message preferredStyle:(SPAlertControllerStyle)preferredStyle animationType:(SPAlertAnimationType)animationType customView:(UIView *)customView customTitleView:(UIView *)customTitleView customCenterView:(UIView *)customCenterView customFooterView:(UIView *)customFooterView{
  355. if (self = [super init]) {
  356. // 是否视图控制器定义它呈现视图控制器的过渡风格(默认为NO)
  357. self.providesPresentationContextTransitionStyle = YES;
  358. self.definesPresentationContext = YES;
  359. self.modalPresentationStyle = UIModalPresentationCustom;
  360. self.transitioningDelegate = self;
  361. _title = title;
  362. _message = message;
  363. self.preferredStyle = preferredStyle;
  364. // 如果是默认动画,preferredStyle为alert时动画默认为fade,preferredStyle为actionShee时动画默认为raiseUp
  365. if (animationType == SPAlertAnimationTypeDefault) {
  366. if (self.preferredStyle == SPAlertControllerStyleAlert) {
  367. animationType= SPAlertAnimationTypeAlpha;
  368. } else if (self.preferredStyle == SPAlertControllerStyleActionSheet) {
  369. animationType = SPAlertAnimationTypeRaiseUp;
  370. } else {
  371. animationType = SPAlertAnimationTypeRaiseUp;
  372. }
  373. }
  374. self.animationType = animationType;
  375. // 添加子控件
  376. [self setupViewsWithCustomView:customView customTitleView:customTitleView customCenterView:customCenterView customFooterView:customFooterView];
  377. self.needBlur = YES;
  378. self.cornerRadiusForAlert = 5;
  379. self.maxTopMarginForActionSheet = isIPhoneX ? 44 : 0;
  380. self.maxMarginForAlert = 20.0;
  381. self.maxNumberOfActionHorizontalArrangementForAlert = 2;
  382. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
  383. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
  384. }
  385. return self;
  386. }
  387. - (void)setupViewsWithCustomView:(UIView *)customView customTitleView:(UIView *)customTitleView customCenterView:(UIView *)customCenterView customFooterView:(UIView *)customFooterView {
  388. UIView *alertView = [[UIView alloc] init];
  389. alertView.frame = self.view.bounds;
  390. alertView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  391. [self.view addSubview:alertView];
  392. _alertView = alertView;
  393. if (!customView) { // 没有自定义整个对话框
  394. UIView *headerBezelView = [[UIView alloc] init];
  395. headerBezelView.translatesAutoresizingMaskIntoConstraints = NO;
  396. [alertView addSubview:headerBezelView];
  397. _headerBezelView = headerBezelView;
  398. // 创建头部scrollView
  399. UIScrollView *headerScrollView = [[UIScrollView alloc] init];
  400. headerScrollView.frame = headerBezelView.bounds;
  401. headerScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  402. headerScrollView.showsHorizontalScrollIndicator = NO;
  403. if ([[UIDevice currentDevice].systemVersion floatValue] >= 11.0) {
  404. // actionTableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
  405. } else {
  406. // Fallback on earlier versions
  407. }
  408. [headerBezelView addSubview:headerScrollView];
  409. _headerScrollView = headerScrollView;
  410. UIView *headerScrollContentView = [[UIView alloc] init];
  411. headerScrollContentView.translatesAutoresizingMaskIntoConstraints = NO;
  412. headerScrollContentView.backgroundColor = actionColor;
  413. [headerScrollView addSubview:headerScrollContentView];
  414. _headerScrollContentView = headerScrollContentView;
  415. if (!customTitleView) { // 不是自定义的titleView
  416. UIView *titleView = [[UIView alloc] init];
  417. titleView.translatesAutoresizingMaskIntoConstraints = NO;
  418. [headerScrollContentView addSubview:titleView];
  419. _titleView = titleView;
  420. UIView *textFieldView = [[UIView alloc] init];
  421. textFieldView.translatesAutoresizingMaskIntoConstraints = NO;
  422. [headerScrollContentView addSubview:textFieldView];
  423. _textFieldView = textFieldView;
  424. if (self.title.length) {
  425. self.titleLabel.text = self.title;
  426. [titleView addSubview:self.titleLabel];
  427. }
  428. if (self.message.length) {
  429. self.detailTitleLabel.text = self.message;
  430. [titleView addSubview:self.detailTitleLabel];
  431. }
  432. } else { // 是自定义的titleView
  433. self.customTitleView = customTitleView;
  434. }
  435. UIView *actionBezelView = [[UIView alloc] init];
  436. actionBezelView.translatesAutoresizingMaskIntoConstraints = NO;
  437. [alertView addSubview:actionBezelView];
  438. _actionBezelView = actionBezelView;
  439. if (!customCenterView) {
  440. UIView *actionBezelContentView = [[UIView alloc] init];
  441. actionBezelContentView.translatesAutoresizingMaskIntoConstraints = NO;
  442. [actionBezelView addSubview:actionBezelContentView];
  443. _actionBezelContentView = actionBezelContentView;
  444. UITableView *actionTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
  445. actionTableView.frame = actionBezelContentView.bounds;
  446. actionTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  447. actionTableView.showsHorizontalScrollIndicator = NO;
  448. actionTableView.alwaysBounceVertical = NO; // tableView内容没有超出contentSize时,禁止滑动
  449. actionTableView.backgroundColor = [UIColor clearColor];
  450. actionTableView.separatorColor = [UIColor clearColor];
  451. actionTableView.dataSource = self;
  452. actionTableView.delegate = self;
  453. if ([[UIDevice currentDevice].systemVersion floatValue] >= 11.0) {
  454. // actionTableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
  455. } else {
  456. // Fallback on earlier versions
  457. }
  458. [actionTableView registerClass:[SPAlertControllerActionCell class] forCellReuseIdentifier:NSStringFromClass([SPAlertControllerActionCell class])];
  459. [actionBezelContentView addSubview:actionTableView];
  460. _actionTableView = actionTableView;
  461. } else {
  462. [self.actionBezelContentView removeFromSuperview];
  463. self.actionBezelContentView = nil;
  464. self.customCenterView = customCenterView;
  465. }
  466. if (!customFooterView) {
  467. UIView *footerView = [[UIView alloc] init];
  468. footerView.translatesAutoresizingMaskIntoConstraints = NO;
  469. [_actionBezelView addSubview:footerView];
  470. _footerView = footerView;
  471. } else {
  472. [self.footerView removeFromSuperview];
  473. self.footerView = nil;
  474. self.customFooterView = customFooterView;
  475. }
  476. [self layoutViewConstraints];
  477. } else {
  478. [self.alertView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  479. [obj removeFromSuperview];
  480. obj = nil;
  481. }];
  482. self.customView = customView;
  483. }
  484. }
  485. - (void)createFooterActionView:(SPAlertAction *)action {
  486. // 这个cell实际上就是一个普通的view,跟tableView没有任何关系,因为cell内部都有现成的控件和布局,直接用这个cell就好,没必要再去自定义一个view,需要注意的是,cell使用了自动布局,contentView会受到影响,看警告对症下药
  487. SPAlertControllerActionCell *footerActionView = [[SPAlertControllerActionCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"footerCell"];
  488. footerActionView.translatesAutoresizingMaskIntoConstraints = NO;
  489. footerActionView.contentView.translatesAutoresizingMaskIntoConstraints = NO;
  490. footerActionView.action = action;
  491. [self.footerView addSubview:footerActionView];
  492. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapFooterActionView:)];
  493. [footerActionView addGestureRecognizer:tap];
  494. }
  495. - (void)tapFooterActionView:(UITapGestureRecognizer *)tap {
  496. if (self.preferredStyle == SPAlertControllerStyleActionSheet) {
  497. if (self.cancelAction.handler) {
  498. self.cancelAction.handler(self.cancelAction);
  499. }
  500. } else {
  501. if (self.actions.count <= _maxNumberOfActionHorizontalArrangementForAlert) {
  502. SPAlertControllerActionCell *footerActionView = (SPAlertControllerActionCell *)tap.view;
  503. NSInteger index = [self.footerView.subviews indexOfObject:footerActionView];
  504. SPAlertAction *action = [self.actions objectAtIndex:index];
  505. // 回调action的block
  506. if (action.handler) {
  507. action.handler(action);
  508. }
  509. }
  510. }
  511. [self dismissViewControllerAnimated:YES completion:^{}];
  512. }
  513. #pragma mark - 布局
  514. - (void)layoutViewConstraints {
  515. if (self.customView) {
  516. return;
  517. }
  518. UIView *alertView = self.alertView;
  519. UIView *headerBezelView = self.headerBezelView;
  520. UIScrollView *headerScrollView = self.headerScrollView;
  521. UIView *headerScrollContentView = self.headerScrollContentView;
  522. UIView *titleView = self.customTitleView ? self.customTitleView : self.titleView;
  523. UIView *textFieldView = self.textFieldView;
  524. UIView *actionBezelView = self.actionBezelView;
  525. UIView *actionBezelContentView = self.customCenterView ? self.customCenterView : self.actionBezelContentView;
  526. UIView *footerView = self.customFooterView ? self.customFooterView : self.footerView;
  527. // 预备相应控件的约束数组
  528. NSMutableArray *headerBezelViewConstraints = [NSMutableArray array];
  529. NSMutableArray *headerScrollContentViewConstraints = [NSMutableArray array];
  530. NSMutableArray *titleViewConstraints = [NSMutableArray array];
  531. NSMutableArray *titleLabelConstraints = [NSMutableArray array];
  532. NSMutableArray *texFieldViewConstraints = [NSMutableArray array];
  533. NSMutableArray *textFieldConstraints = [NSMutableArray array];
  534. NSMutableArray *actionBezelViewConstraints = [NSMutableArray array];
  535. NSMutableArray *actionBezelContentViewConstraints = [NSMutableArray array];
  536. NSMutableArray *footerViewConstraints = [NSMutableArray array];
  537. NSMutableArray *footerActionViewConstraints = [NSMutableArray array];
  538. // 移除存在的约束
  539. if (self.headerBezelViewConstraints) {
  540. [alertView removeConstraints:self.headerBezelViewConstraints];
  541. self.headerBezelViewConstraints = nil;
  542. }
  543. if (self.headerScrollContentViewConstraints) {
  544. [headerScrollView removeConstraints:self.headerScrollContentViewConstraints];
  545. self.headerScrollContentViewConstraints = nil;
  546. }
  547. if (self.titleViewConstraints) {
  548. [headerScrollContentView removeConstraints:self.titleViewConstraints];
  549. self.titleViewConstraints = nil;
  550. }
  551. if (self.titleLabelConstraints) {
  552. [titleView removeConstraints:self.titleLabelConstraints];
  553. self.titleLabelConstraints = nil;
  554. }
  555. if (self.textFieldViewConstraints) {
  556. [headerScrollContentView removeConstraints:self.textFieldViewConstraints];
  557. self.textFieldViewConstraints = nil;
  558. }
  559. if (self.textFieldConstraints) {
  560. [textFieldView removeConstraints:self.textFieldConstraints];
  561. self.textFieldConstraints = nil;
  562. }
  563. if (self.actionBezelViewConstraints) {
  564. [alertView removeConstraints:self.actionBezelViewConstraints];
  565. self.actionBezelViewConstraints = nil;
  566. }
  567. if (self.actionBezelContentViewConstraints) {
  568. [actionBezelView removeConstraints:self.actionBezelContentViewConstraints];
  569. self.actionBezelContentViewConstraints = nil;
  570. }
  571. if (self.footerViewConstraints) {
  572. [actionBezelView removeConstraints:self.footerViewConstraints];
  573. self.footerViewConstraints = nil;
  574. }
  575. if (self.footerActionViewConstraints) {
  576. [footerView removeConstraints:self.footerActionViewConstraints];
  577. self.footerActionViewConstraints = nil;
  578. }
  579. CGFloat margin = 15;
  580. CGFloat footerTopMargin = self.cancelAction ? 6.0 : 0.0;
  581. CGFloat headerActionPadding = (!titleView.subviews.count || !self.actions.count) ? 0 : 0.5;
  582. // 计算好actionBezelView的高度, 本也可以让设置每个子控件都高度约束,以及顶底约束和子控件之间的间距,这样便可以把actionBezelView的高度撑起来,但是这里要比较一下actionBezelView和headerBezelView的高度优先级,所以父控件设置高度比较方便,谁的优先级高,谁展示的内容就更多
  583. CGFloat actionBezelHeight = [self actionBezelHeight:footerTopMargin];
  584. [headerBezelViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[headerBezelView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(headerBezelView)]];
  585. [headerBezelViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:|-0-[headerBezelView]-%f-[actionBezelView]-0-|",headerActionPadding] options:0 metrics:nil views:NSDictionaryOfVariableBindings(headerBezelView,actionBezelView)]];
  586. // headerBezelView的高度最大为(self.view.bounds.size.height-itemHeight)
  587. [headerBezelViewConstraints addObject:[NSLayoutConstraint constraintWithItem:headerBezelView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:self.view.bounds.size.height-actionHeight]];
  588. // 暂时先初始化headerBezelView的高度约束
  589. NSLayoutConstraint *headerBezelViewContsraintHeight = [NSLayoutConstraint constraintWithItem:headerBezelView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:0];
  590. /// 设置优先级
  591. headerBezelViewContsraintHeight.priority = 998.0;
  592. [headerBezelViewConstraints addObject:headerBezelViewContsraintHeight];
  593. [alertView addConstraints:headerBezelViewConstraints];
  594. // 设置actionScrollContentView的相关约束,值得注意的是不能仅仅设置上下左右间距为0就完事了,对于scrollView的contentView, autoLayout布局必须设置宽或高约束
  595. [headerScrollContentViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[headerScrollContentView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(headerScrollContentView)]];
  596. [headerScrollContentViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[headerScrollContentView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(headerScrollContentView)]];
  597. [headerScrollContentViewConstraints addObject:[NSLayoutConstraint constraintWithItem:headerScrollContentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:headerScrollView attribute:NSLayoutAttributeWidth multiplier:1.f constant:0]];
  598. if (_titleLabel.text.length || _detailTitleLabel.text.length) {
  599. // 保证headerScrollContentView的高度最小为actionHeight
  600. [headerScrollContentViewConstraints addObject:[NSLayoutConstraint constraintWithItem:headerScrollContentView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:actionHeight]];
  601. }
  602. [headerScrollView addConstraints:headerScrollContentViewConstraints];
  603. if (!self.customTitleView) {
  604. [titleViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[titleView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(titleView)]];
  605. [titleViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[titleView]-0-[textFieldView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(titleView,textFieldView)]];
  606. [headerScrollContentView addConstraints:titleViewConstraints];
  607. NSArray *labels = titleView.subviews;
  608. [labels enumerateObjectsUsingBlock:^(UILabel *label, NSUInteger idx, BOOL * _Nonnull stop) {
  609. // 左右间距
  610. [titleLabelConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"H:|-(==margin)-[label]-(==margin)-|"] options:0 metrics:@{@"margin":@(margin)} views:NSDictionaryOfVariableBindings(label)]];
  611. // 第一个子控件顶部间距
  612. if (idx == 0) {
  613. [titleLabelConstraints addObject:[NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:titleView attribute:NSLayoutAttributeTop multiplier:1.f constant:margin]];
  614. }
  615. // 最后一个子控件底部间距
  616. if (idx == labels.count - 1) {
  617. [titleLabelConstraints addObject:[NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:titleView attribute:NSLayoutAttributeBottom multiplier:1.f constant:-margin]];
  618. }
  619. // 子控件之间的垂直间距
  620. if (idx > 0) {
  621. NSLayoutConstraint *paddingConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:labels[idx - 1] attribute:NSLayoutAttributeBottom multiplier:1.f constant:margin*0.5];
  622. [titleLabelConstraints addObject:paddingConstraint];
  623. }
  624. }];
  625. [titleView addConstraints:titleLabelConstraints];
  626. CGFloat textFieldViewHeight = 0;
  627. CGFloat textFieldMargin = 15;
  628. CGFloat textFiledHeight = 26;
  629. if (self.textFields.count) {
  630. textFieldViewHeight = self.textFields.count * textFiledHeight + textFieldMargin + 0.5 * textFieldMargin;
  631. }
  632. [texFieldViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[textFieldView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(textFieldView)]];
  633. [texFieldViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[titleView]-0-[textFieldView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(titleView,textFieldView)]];
  634. [texFieldViewConstraints addObject:[NSLayoutConstraint constraintWithItem:textFieldView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:textFieldViewHeight]];
  635. [headerScrollContentView addConstraints:texFieldViewConstraints];
  636. NSArray *textFields = textFieldView.subviews;
  637. [textFields enumerateObjectsUsingBlock:^(UITextField *textField, NSUInteger idx, BOOL * _Nonnull stop) {
  638. // 左右间距
  639. [textFieldConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"H:|-(==textFieldMargin)-[textField]-(==textFieldMargin)-|"] options:0 metrics:@{@"textFieldMargin":@(textFieldMargin)} views:NSDictionaryOfVariableBindings(textField)]];
  640. [textFieldConstraints addObject:[NSLayoutConstraint constraintWithItem:textField attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:textFiledHeight]];
  641. // 第一个子控件顶部间距
  642. if (idx == 0) {
  643. [textFieldConstraints addObject:[NSLayoutConstraint constraintWithItem:textField attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:textFieldView attribute:NSLayoutAttributeTop multiplier:1.f constant:textFieldMargin*0.5]];
  644. }
  645. // 最后一个子控件底部间距
  646. if (idx == textFields.count - 1) {
  647. [textFieldConstraints addObject:[NSLayoutConstraint constraintWithItem:textField attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:textFieldView attribute:NSLayoutAttributeBottom multiplier:1.f constant:-textFieldMargin]];
  648. }
  649. // 子控件之间的垂直间距
  650. if (idx > 0) {
  651. NSLayoutConstraint *paddingConstraint = [NSLayoutConstraint constraintWithItem:textField attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:textFields[idx - 1] attribute:NSLayoutAttributeBottom multiplier:1.f constant:0];
  652. [textFieldConstraints addObject:paddingConstraint];
  653. }
  654. }];
  655. [textFieldView addConstraints:textFieldConstraints];
  656. } else {
  657. // 自定义titleView时,这里的titleView就是customTitleView
  658. [titleViewConstraints addObject:[NSLayoutConstraint constraintWithItem:titleView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:_customTitleViewSize.width]];
  659. [titleViewConstraints addObject:[NSLayoutConstraint constraintWithItem:titleView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:headerScrollContentView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
  660. [titleViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[titleView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(titleView)]];
  661. [titleViewConstraints addObject:[NSLayoutConstraint constraintWithItem:titleView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:_customTitleViewSize.height]];
  662. [headerScrollContentView addConstraints:titleViewConstraints];
  663. }
  664. // 先强制布局一次,否则下面拿到的CGRectGetMaxY(titleView.frame)还没有值
  665. [headerBezelView setNeedsLayout]; // 确保一定走layoutSubViews(异步)
  666. [headerBezelView layoutIfNeeded]; // 立即调用layoutSubViews
  667. // 设置headerBezelView的高度(这个高度同样可以通过计算titleLabel和detailTitleLabel的文字高度计算出来,但是那样计算出来的高度会有零点几的误差,只要差了一点,有可能scrollView即便内容没有超过contentSize,仍然能够滑动)
  668. CGRect rect;
  669. if (!self.customTitleView) {
  670. rect = self.textFields.count ? textFieldView.frame : titleView.frame;
  671. } else {
  672. rect = titleView.frame;
  673. }
  674. headerBezelViewContsraintHeight.constant = CGRectGetMaxY(rect);
  675. // ----------------------------------------------------------------------------
  676. [actionBezelViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[actionBezelView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(actionBezelView)]];
  677. [actionBezelViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:|-0-[headerBezelView]-%f-[actionBezelView]-0-|",headerActionPadding] options:0 metrics:nil views:NSDictionaryOfVariableBindings(actionBezelView,headerBezelView)]];
  678. NSLayoutConstraint *actionBezelViewHeightContraint = [NSLayoutConstraint constraintWithItem:actionBezelView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:actionBezelHeight];
  679. // 设置优先级,要比上面headerBezelViewContraintHeight的优先级低,这样当文字过多和action同时过多时,都超出了最大限制,此时优先展示文字
  680. actionBezelViewHeightContraint.priority = 997.0f;
  681. // 计算最小高度
  682. CGFloat minActionHeight = [self minActionHeight:footerTopMargin];
  683. NSLayoutConstraint *minActionHeightConstraint = [NSLayoutConstraint constraintWithItem:actionBezelView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:minActionHeight];
  684. [actionBezelViewConstraints addObject:minActionHeightConstraint];
  685. [actionBezelViewConstraints addObject:actionBezelViewHeightContraint];
  686. [alertView addConstraints:actionBezelViewConstraints];
  687. // 如果customCenterView有值,actionBezelContentView就是customCenterView
  688. if (!self.customCenterView) {
  689. [actionBezelContentViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[actionBezelContentView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(actionBezelContentView)]];
  690. } else {
  691. [actionBezelContentViewConstraints addObject:[NSLayoutConstraint constraintWithItem:actionBezelContentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:_customCenterViewSize.width]];
  692. [actionBezelContentViewConstraints addObject:[NSLayoutConstraint constraintWithItem:actionBezelContentView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:actionBezelView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
  693. }
  694. [actionBezelContentViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:|-0-[actionBezelContentView]-%f-[footerView]-0-|",footerTopMargin] options:0 metrics:nil views:NSDictionaryOfVariableBindings(actionBezelContentView,footerView)]];
  695. [actionBezelView addConstraints:actionBezelContentViewConstraints];
  696. if (!self.customFooterView) { // 不是自定义的footerView
  697. [footerViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[footerView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(footerView)]];
  698. [footerViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:|-0-[actionBezelContentView]-%f-[footerView]-0-|",footerTopMargin] options:0 metrics:nil views:NSDictionaryOfVariableBindings(footerView,actionBezelContentView)]];
  699. // 这个条件判断需不需要footerView,不满足条件footerView的高度就给0
  700. if ((self.preferredStyle == SPAlertControllerStyleActionSheet && self.cancelAction) || (self.preferredStyle == SPAlertControllerStyleAlert && (self.actions.count <= _maxNumberOfActionHorizontalArrangementForAlert) && self.actions.count)) {
  701. [footerViewConstraints addObject:[NSLayoutConstraint constraintWithItem:footerView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:actionHeight]];
  702. } else {
  703. // 不满足条件,高度置为0,注意给0和不给高度是两回事,给0至少footerView有一个高度约束,不给的话就没有高度约束,这会导致tableView的底部间距设置无效,从而导致tableView不显示
  704. [footerViewConstraints addObject:[NSLayoutConstraint constraintWithItem:footerView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:0]];
  705. }
  706. [actionBezelView addConstraints:footerViewConstraints];
  707. NSArray *footerActionViews = footerView.subviews;
  708. if (footerActionViews.count && ((self.preferredStyle == SPAlertControllerStyleActionSheet && self.cancelAction) || (self.preferredStyle == SPAlertControllerStyleAlert && self.actions.count <= _maxNumberOfActionHorizontalArrangementForAlert))) {
  709. [footerActionViews enumerateObjectsUsingBlock:^(SPAlertControllerActionCell *footerActionView, NSUInteger idx, BOOL * _Nonnull stop) {
  710. // 设置footerActionView的上下间距
  711. [footerActionViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[footerActionView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(footerActionView)]];
  712. // 第一个footerActionView的左间距
  713. if (idx == 0) {
  714. [footerActionViewConstraints addObject:[NSLayoutConstraint constraintWithItem:footerActionView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:footerView attribute:NSLayoutAttributeLeft multiplier:1.f constant:0]];
  715. }
  716. // 最后一个itemView的右间距
  717. if (idx == footerActionViews.count-1) {
  718. [footerActionViewConstraints addObject:[NSLayoutConstraint constraintWithItem:footerActionView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:footerView attribute:NSLayoutAttributeRight multiplier:1.f constant:-0]];
  719. }
  720. if (idx > 0) {
  721. // 子控件之间的水平间距
  722. [footerActionViewConstraints addObject:[NSLayoutConstraint constraintWithItem:footerActionView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:footerActionViews[idx - 1] attribute:NSLayoutAttributeRight multiplier:1.f constant:0.5]];
  723. // 等宽
  724. [footerActionViewConstraints addObject:[NSLayoutConstraint constraintWithItem:footerActionView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:footerActionViews[idx - 1] attribute:NSLayoutAttributeWidth multiplier:1.f constant:0]];
  725. }
  726. // 在这里,这个cell的contentView没有任何作用,但是为了消除控制台打印的contentView应该给一个高度的警告,这里给一个0
  727. [footerActionView.contentView addConstraint:[NSLayoutConstraint constraintWithItem:footerActionView.contentView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:0]];
  728. }];
  729. [footerView addConstraints:footerActionViewConstraints];
  730. }
  731. } else { // 是自定义的footerView
  732. // 自定义titleView时,这里的titleView就是customTitleView
  733. [footerViewConstraints addObject:[NSLayoutConstraint constraintWithItem:footerView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:_customFooterViewSize.width]];
  734. actionBezelView.backgroundColor = [UIColor redColor];
  735. actionBezelContentView.backgroundColor = [UIColor greenColor];
  736. if (!self.actions.count) { // 一个action都没有,也就是actionBezelContentView高度为0
  737. [footerViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0.5-[footerView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(footerView)]];
  738. } else { // 有action,也就是actionBezelContentView必须要有高度,因此这里footerView要给高度,系统才能计算出actionBezelContentView的剩余空间
  739. [footerViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:@"V:|-0-[actionBezelContentView]-%f-[footerView]-0-|",footerTopMargin] options:0 metrics:nil views:NSDictionaryOfVariableBindings(footerView,actionBezelContentView)]];
  740. [footerViewConstraints addObject:[NSLayoutConstraint constraintWithItem:footerView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:_customFooterViewSize.height]];
  741. }
  742. [footerViewConstraints addObject:[NSLayoutConstraint constraintWithItem:footerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:actionBezelView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
  743. [actionBezelView addConstraints:footerViewConstraints];
  744. }
  745. // 强制布局,立刻产生frame
  746. [self.view layoutIfNeeded];
  747. // 如果有文本输入框,让头部scrollVIew自动滚动到最底端(这是为了当文字过多时,可以立即滚动到第一个输入框的位置)
  748. if (self.textFields.count) {
  749. UITextField *firstTextField = self.textFields.firstObject;
  750. [self.headerScrollView scrollRectToVisible:firstTextField.frame animated:YES];
  751. }
  752. self.headerBezelViewConstraints = headerBezelViewConstraints;
  753. self.headerScrollContentViewConstraints = headerScrollContentViewConstraints;
  754. self.titleViewConstraints = titleViewConstraints;
  755. self.titleLabelConstraints = titleLabelConstraints;
  756. self.textFieldViewConstraints = texFieldViewConstraints;
  757. self.textFieldConstraints = textFieldConstraints;
  758. self.actionBezelViewConstraints = actionBezelViewConstraints;
  759. self.actionBezelContentViewConstraints = actionBezelContentViewConstraints;
  760. self.footerViewConstraints = footerViewConstraints;
  761. self.footerActionViewConstraints = footerActionViewConstraints;
  762. }
  763. // 布局自定义的view
  764. - (void)layoutCustomView {
  765. UIView *alertView = self.alertView;
  766. UIView *customView = self.customView;
  767. NSMutableArray *customViewConstraints = [NSMutableArray array];
  768. if (self.customViewConstraints) {
  769. [alertView removeConstraints:self.customViewConstraints];
  770. self.customViewConstraints = nil;
  771. }
  772. [customViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[customView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(customView)]];
  773. [customViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[customView]-0-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(customView)]];
  774. [alertView addConstraints:customViewConstraints];
  775. self.customViewConstraints = customViewConstraints;
  776. }
  777. - (CGFloat)actionBezelHeight:(CGFloat)footerTopMargin {
  778. CGFloat actionBezelHeight = 0;
  779. // 计算actionBezelview的高度
  780. if (self.actions.count) {
  781. if (self.preferredStyle == SPAlertControllerStyleActionSheet) {
  782. if (self.cancelAction) { // 有取消按钮肯定没有自定义footerView
  783. if (self.actions.count > 1) {
  784. actionBezelHeight = self.actions.count*actionHeight+footerTopMargin;
  785. } else {
  786. if (self.customCenterView) { // 当有自定义的customCenterView时,最多只会有1个action,在addAction:方法里做了处理
  787. actionBezelHeight = actionHeight+footerTopMargin+_customCenterViewSize.height;
  788. } else {
  789. actionBezelHeight = actionHeight+footerTopMargin;
  790. }
  791. }
  792. } else {
  793. if (self.customCenterView) {
  794. actionBezelHeight = _customCenterViewSize.height;
  795. } else {
  796. if (self.customFooterView) {
  797. actionBezelHeight = self.actions.count*actionHeight+_customFooterViewSize.height;
  798. } else {
  799. actionBezelHeight = self.actions.count*actionHeight;
  800. }
  801. }
  802. }
  803. } else if (self.preferredStyle == SPAlertControllerStyleAlert) {
  804. if (self.actions.count <= _maxNumberOfActionHorizontalArrangementForAlert) {
  805. if (!self.customCenterView) { // 当没有自定义的customCenterView时,最多只会有_maxNumberOfActionHorizontalArrangementForAlert个action,在addAction:方法里做了处理
  806. if (self.customFooterView) {
  807. actionBezelHeight = _customFooterViewSize.height + actionHeight;
  808. } else {
  809. actionBezelHeight = actionHeight;
  810. }
  811. } else { // _maxNumberOfActionHorizontalArrangementForAlert个以上action且customCenterView有值
  812. actionBezelHeight = _customCenterViewSize.height + actionHeight;
  813. }
  814. } else {
  815. if (self.customFooterView) {
  816. actionBezelHeight = _customFooterViewSize.height+self.actions.count*actionHeight;
  817. } else {
  818. actionBezelHeight = self.actions.count*actionHeight;
  819. }
  820. }
  821. }
  822. } else {
  823. if (self.customCenterView) {
  824. actionBezelHeight = _customCenterViewSize.height;
  825. } else {
  826. if (self.customFooterView) {
  827. actionBezelHeight = _customFooterViewSize.height;
  828. }
  829. }
  830. }
  831. return actionBezelHeight;
  832. }
  833. - (CGFloat)minActionHeight:(CGFloat)footerTopMargin {
  834. CGFloat minActionHeight = 0;
  835. if (self.cancelAction) {
  836. if (self.actions.count > 3) { // 有一个取消按钮且其余按钮在3个或3个以上
  837. minActionHeight = 3.5*actionHeight+footerTopMargin;
  838. } else {
  839. minActionHeight = self.actions.count * actionHeight + footerTopMargin;
  840. }
  841. } else {
  842. if (self.actions.count > 3) { // 没有取消按钮,其余按钮在3个或3个以上
  843. if (self.actions.count <= _maxNumberOfActionHorizontalArrangementForAlert) {
  844. minActionHeight = minActionHeight;
  845. } else {
  846. minActionHeight = 3.5 * actionHeight;
  847. }
  848. } else {
  849. if (self.preferredStyle == SPAlertControllerStyleAlert) {
  850. if (self.actions.count) {
  851. minActionHeight = actionHeight;
  852. }
  853. } else {
  854. minActionHeight = self.actions.count * actionHeight;
  855. }
  856. }
  857. }
  858. // 如果是自定义的footerView,高度还要在上面计算的基础上加上footerView的高度
  859. if (self.customFooterView) {
  860. minActionHeight = minActionHeight+_customFooterViewSize.height;
  861. }
  862. return minActionHeight;
  863. }
  864. #pragma mark - setter
  865. - (void)setTitle:(NSString *)title {
  866. _title = [title copy];
  867. self.titleLabel.text = title;
  868. if (!self.titleLabel.superview) {
  869. if (_detailTitleLabel) {
  870. [self.titleView insertSubview:_titleLabel belowSubview:_detailTitleLabel];
  871. } else {
  872. [self.titleView addSubview:_titleLabel];
  873. }
  874. }
  875. [self.view setNeedsUpdateConstraints];
  876. }
  877. - (void)setMessage:(NSString *)message {
  878. _message = [message copy];
  879. self.detailTitleLabel.text = message;
  880. if (!self.detailTitleLabel.superview) {
  881. [self.titleView addSubview:_detailTitleLabel];
  882. }
  883. [self.view setNeedsUpdateConstraints];
  884. }
  885. - (void)setTitleColor:(UIColor *)titleColor {
  886. _titleColor = titleColor;
  887. self.titleLabel.textColor = titleColor;
  888. }
  889. - (void)setTitleFont:(UIFont *)titleFont {
  890. _titleFont = titleFont;
  891. self.titleLabel.font = titleFont;
  892. }
  893. - (void)setMessageColor:(UIColor *)messageColor {
  894. _messageColor = messageColor;
  895. self.detailTitleLabel.textColor = messageColor;
  896. }
  897. - (void)setMessageFont:(UIFont *)messageFont {
  898. _messageFont = messageFont;
  899. self.detailTitleLabel.font = messageFont;
  900. }
  901. - (void)setNeedBlur:(BOOL)needBlur {
  902. _needBlur = needBlur;
  903. if (!needBlur) {
  904. self.alertView.backgroundColor = [UIColor colorWithDisplayP3Red:200.0/255.0 green:200.0/255.0 blue:200.0/255.0 alpha:1];
  905. [self.alertEffectView removeFromSuperview];
  906. self.alertEffectView = nil;
  907. } else {
  908. UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight];
  909. UIVisualEffectView *alertEffectView = [[UIVisualEffectView alloc] initWithEffect:effect];
  910. alertEffectView.frame = self.alertView.bounds;
  911. alertEffectView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  912. [self.alertView insertSubview:alertEffectView atIndex:0];
  913. }
  914. }
  915. - (void)setMaxTopMarginForActionSheet:(CGFloat)maxTopMarginForActionSheet {
  916. _maxTopMarginForActionSheet = maxTopMarginForActionSheet;
  917. if (self.customView) {
  918. [self layoutCustomView];
  919. } else {
  920. [self layoutViewConstraints];
  921. }
  922. }
  923. - (void)setMaxMarginForAlert:(CGFloat)maxMarginForAlert {
  924. _maxMarginForAlert = maxMarginForAlert;
  925. if (self.customView) {
  926. [self layoutCustomView];
  927. } else {
  928. // 检查一下customTitleViewSize和customCenterView的宽度是否大于了新的对话框的宽度
  929. if (self.preferredStyle == SPAlertControllerStyleAlert) {
  930. if (_customTitleViewSize.width >= ScreenWidth-2*maxMarginForAlert) {
  931. _customTitleViewSize.width = ScreenWidth-2*maxMarginForAlert;
  932. }
  933. if (_customCenterViewSize.width >= ScreenWidth-2*maxMarginForAlert) {
  934. _customCenterViewSize.width = ScreenWidth-2*maxMarginForAlert;
  935. }
  936. if (_customFooterViewSize.width >= ScreenWidth-2*maxMarginForAlert) {
  937. _customFooterViewSize.width = ScreenWidth-2*maxMarginForAlert;
  938. }
  939. } else {
  940. if (_customTitleViewSize.width >= ScreenWidth) {
  941. _customTitleViewSize.width = ScreenWidth;
  942. }
  943. if (_customCenterViewSize.width >= ScreenWidth) {
  944. _customCenterViewSize.width = ScreenWidth;
  945. }
  946. if (_customFooterViewSize.width >= ScreenWidth) {
  947. _customFooterViewSize.width = ScreenWidth;
  948. }
  949. }
  950. [self layoutViewConstraints];
  951. }
  952. }
  953. - (void)setMaxNumberOfActionHorizontalArrangementForAlert:(NSInteger)maxNumberOfActionHorizontalArrangementForAlert {
  954. _maxNumberOfActionHorizontalArrangementForAlert = maxNumberOfActionHorizontalArrangementForAlert;
  955. if (self.customFooterView) {
  956. _maxNumberOfActionHorizontalArrangementForAlert = -1;
  957. }
  958. }
  959. - (void)setCornerRadiusForAlert:(CGFloat)cornerRadiusForAlert {
  960. _cornerRadiusForAlert = cornerRadiusForAlert;
  961. if (self.preferredStyle == SPAlertControllerStyleAlert) {
  962. self.view.layer.cornerRadius = cornerRadiusForAlert;
  963. self.view.layer.masksToBounds = YES;
  964. }
  965. }
  966. - (void)setOffsetYForAlert:(CGFloat)offsetYForAlert {
  967. _offsetYForAlert = offsetYForAlert;
  968. }
  969. - (void)setCustomView:(UIView *)customView {
  970. _customView = customView;
  971. [customView layoutIfNeeded];
  972. _customViewSize = customView.frame.size;
  973. customView.translatesAutoresizingMaskIntoConstraints = NO;
  974. [self.alertView addSubview:customView];
  975. [self layoutCustomView];
  976. }
  977. - (void)setCustomTitleView:(UIView *)customTitleView {
  978. _customTitleView = customTitleView;
  979. [customTitleView layoutIfNeeded];
  980. _customTitleViewSize = customTitleView.bounds.size;
  981. if (_customTitleViewSize.width <= 0 || _customTitleViewSize.width >= ScreenWidth-2*_maxMarginForAlert) {
  982. _customTitleViewSize.width = ScreenWidth-2*_maxMarginForAlert;
  983. }
  984. if (_customTitleViewSize.height <= 0) {
  985. NSLog(@"你的customTitleView高度为小于等于0,请设置一个高度");
  986. }
  987. customTitleView.translatesAutoresizingMaskIntoConstraints = NO;
  988. [self.headerScrollContentView addSubview:customTitleView];
  989. }
  990. - (void)setCustomCenterView:(UIView *)customCenterView {
  991. _customCenterView = customCenterView;
  992. [customCenterView layoutIfNeeded];
  993. _customCenterViewSize = customCenterView.bounds.size;
  994. if (_customCenterViewSize.width <= 0 || _customCenterViewSize.width >= ScreenWidth-2*_maxMarginForAlert) {
  995. _customCenterViewSize.width = ScreenWidth-2*_maxMarginForAlert;
  996. }
  997. if (_customCenterViewSize.height <= 0) {
  998. NSLog(@"你的customCenterView高度为小于等于0,请设置一个高度");
  999. }
  1000. customCenterView.translatesAutoresizingMaskIntoConstraints = NO;
  1001. [self.actionBezelView addSubview:customCenterView];
  1002. }
  1003. - (void)setCustomFooterView:(UIView *)customFooterView {
  1004. _customFooterView = customFooterView;
  1005. [customFooterView layoutIfNeeded];
  1006. _customFooterViewSize = customFooterView.bounds.size;
  1007. if (_customFooterViewSize.width <= 0 || _customFooterViewSize.width >= ScreenWidth-2*_maxMarginForAlert) {
  1008. _customFooterViewSize.width = ScreenWidth-2*_maxMarginForAlert;
  1009. }
  1010. if (_customFooterViewSize.height <= 0) {
  1011. NSLog(@"你的customFooterView高度为小于等于0,请设置一个高度");
  1012. }
  1013. customFooterView.translatesAutoresizingMaskIntoConstraints = NO;
  1014. [self.actionBezelView addSubview:customFooterView];
  1015. }
  1016. #pragma mark - getter
  1017. - (UILabel *)titleLabel {
  1018. if (!_titleLabel) {
  1019. _titleLabel = [[UILabel alloc] init];
  1020. _titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
  1021. _titleLabel.textAlignment = NSTextAlignmentCenter;
  1022. _titleLabel.numberOfLines = 0;
  1023. _titleLabel.font = [UIFont boldSystemFontOfSize:17];
  1024. // 设置垂直方向的抗压缩优先级,优先级越高越不容易被压缩,默认的优先级是750
  1025. [_titleLabel setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical];
  1026. [_titleLabel sizeToFit];
  1027. }
  1028. return _titleLabel;
  1029. }
  1030. - (UILabel *)detailTitleLabel {
  1031. if (!_detailTitleLabel) {
  1032. _detailTitleLabel = [[UILabel alloc] init];
  1033. _detailTitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
  1034. _detailTitleLabel.textAlignment = NSTextAlignmentCenter;
  1035. _detailTitleLabel.numberOfLines = 0;
  1036. _detailTitleLabel.font = [UIFont systemFontOfSize:14];
  1037. _detailTitleLabel.alpha = 0.5;
  1038. // 设置垂直方向的抗压缩优先级,优先级越高越不容易被压缩,默认的优先级是750
  1039. [_detailTitleLabel setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical];
  1040. [_detailTitleLabel sizeToFit];
  1041. }
  1042. return _detailTitleLabel;
  1043. }
  1044. - (NSArray<SPAlertAction *> *)actions {
  1045. if (!_actions) {
  1046. _actions = [NSArray array];
  1047. }
  1048. return _actions;
  1049. }
  1050. - (NSArray<UITextField *> *)textFields {
  1051. if (!_textFields) {
  1052. _textFields = [NSArray array];
  1053. }
  1054. return _textFields;
  1055. }
  1056. #pragma mark - 通知
  1057. - (void)keyboardWillShow:(NSNotification *)notification {
  1058. if (!self.keyboardShow) { // 如果键盘是隐藏状态,本次弹出键盘才去改变alert的中心偏移,否则如果键盘已经是显示状态,什么都不做
  1059. CGRect keyboardEndFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
  1060. CGFloat keyboardEndY = keyboardEndFrame.origin.y;
  1061. CGFloat diff = fabs((self.view.center.y-keyboardEndY*0.5));
  1062. // 改变alertView的中心y值,以至于不被键盘遮挡
  1063. self.offsetYForAlert = -diff;
  1064. }
  1065. self.keyboardShow = YES;
  1066. }
  1067. - (void)keyboardDidHide:(NSNotification *)notification {
  1068. self.keyboardShow = NO;
  1069. }
  1070. #pragma mark - UIViewControllerTransitioningDelegate
  1071. - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
  1072. return [SPAlertAnimation animationIsPresenting:YES];
  1073. }
  1074. - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
  1075. [self.view endEditing:YES];
  1076. return [SPAlertAnimation animationIsPresenting:NO];
  1077. }
  1078. - (nullable UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(nullable UIViewController *)presenting sourceViewController:(UIViewController *)source NS_AVAILABLE_IOS(8_0) {
  1079. return [[SPAlertPresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting];
  1080. }
  1081. @end
  1082. #pragma mark ---------------------------- SPAlertController end --------------------------------
  1083. #pragma mark ---------------------------- SPAlertPresentationController begin --------------------------------
  1084. @interface SPAlertPresentationController()
  1085. @property (nonatomic, strong) UIView *overlayView;
  1086. @property (nonatomic, strong) NSMutableArray *presentedViewConstraints;
  1087. @end
  1088. @implementation SPAlertPresentationController
  1089. - (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController {
  1090. if (self = [super initWithPresentedViewController:presentedViewController presentingViewController:presentingViewController]) {
  1091. self.presentedView.translatesAutoresizingMaskIntoConstraints = NO;
  1092. }
  1093. return self;
  1094. }
  1095. - (void)containerViewWillLayoutSubviews {
  1096. [super containerViewWillLayoutSubviews];
  1097. SPAlertController *alertController = (SPAlertController *)self.presentedViewController;
  1098. CGFloat maxTopMarginForActionSheet = alertController.maxTopMarginForActionSheet;
  1099. CGFloat maxMarginForAlert = alertController.maxMarginForAlert;
  1100. CGFloat topMarginForAlert = isIPhoneX ? (maxMarginForAlert+44):maxMarginForAlert;
  1101. CGFloat bottomMarginForAlert = isIPhoneX ? (maxMarginForAlert+34):maxMarginForAlert;
  1102. UIView *presentedView = self.presentedView;
  1103. NSMutableArray *presentedViewConstraints = [NSMutableArray array];
  1104. if (self.presentedViewConstraints) {
  1105. [self.containerView removeConstraints:self.presentedViewConstraints];
  1106. self.presentedViewConstraints = nil;
  1107. }
  1108. UIView *customView = alertController.customView;
  1109. if (!customView) { // 非自定义
  1110. if (alertController.preferredStyle == SPAlertControllerStyleActionSheet) {
  1111. [presentedViewConstraints addObject: [NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:ScreenWidth]];
  1112. [presentedViewConstraints addObject: [NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
  1113. if (alertController.animationType == SPAlertAnimationTypeDropDown) {
  1114. [presentedViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[presentedView]-(>=alertBottomMargin)-|" options:0 metrics:@{@"maxTopMarginForActionSheet":@(maxTopMarginForActionSheet),@"alertBottomMargin":@(alertBottomMargin)} views:NSDictionaryOfVariableBindings(presentedView)]];
  1115. } else {
  1116. [presentedViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=maxTopMarginForActionSheet)-[presentedView]-(==alertBottomMargin)-|" options:0 metrics:@{@"maxTopMarginForActionSheet":@(maxTopMarginForActionSheet),@"alertBottomMargin":@(alertBottomMargin)} views:NSDictionaryOfVariableBindings(presentedView)]];
  1117. }
  1118. } else if (alertController.preferredStyle == SPAlertControllerStyleAlert) {
  1119. [presentedViewConstraints addObject: [NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:(MIN(ScreenWidth, ScreenHeight)-2*maxMarginForAlert)]];
  1120. [presentedViewConstraints addObject: [NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
  1121. [presentedViewConstraints addObject:[NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self.containerView attribute:NSLayoutAttributeTop multiplier:1.0f constant:topMarginForAlert]];
  1122. [presentedViewConstraints addObject:[NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationLessThanOrEqual toItem:self.containerView attribute:NSLayoutAttributeBottom multiplier:1.0f constant:-bottomMarginForAlert]];
  1123. [presentedViewConstraints addObject:[NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0]];
  1124. [presentedViewConstraints addObject:[NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:alertController.offsetYForAlert]];
  1125. NSLog(@"--- %f",alertController.offsetYForAlert);
  1126. }
  1127. } else { // 自定义
  1128. CGFloat alertH = alertController.customViewSize.height;
  1129. CGFloat alertW = alertController.customViewSize.width;
  1130. if (alertH > (self.containerView.bounds.size.height-maxTopMarginForActionSheet)) {
  1131. alertH = (self.containerView.bounds.size.height-(topMarginForAlert+bottomMarginForAlert));
  1132. }
  1133. if (alertController.preferredStyle == SPAlertControllerStyleActionSheet) {
  1134. [presentedViewConstraints addObject: [NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:alertW]];
  1135. [presentedViewConstraints addObject: [NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
  1136. if (alertController.animationType == SPAlertAnimationTypeDropDown) {
  1137. [presentedViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(==maxTopMarginForActionSheet)-[presentedView]-(>=alertBottomMargin)-|" options:0 metrics:@{@"maxTopMarginForActionSheet":@(maxTopMarginForActionSheet),@"alertBottomMargin":@(alertBottomMargin)} views:NSDictionaryOfVariableBindings(presentedView)]];
  1138. } else {
  1139. [presentedViewConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=maxTopMarginForActionSheet)-[presentedView]-(==alertBottomMargin)-|" options:0 metrics:@{@"maxTopMarginForActionSheet":@(maxTopMarginForActionSheet),@"alertBottomMargin":@(alertBottomMargin)} views:NSDictionaryOfVariableBindings(presentedView)]];
  1140. }
  1141. } else {
  1142. [presentedViewConstraints addObject: [NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:alertW]];
  1143. [presentedViewConstraints addObject: [NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
  1144. [presentedViewConstraints addObject:[NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self.containerView attribute:NSLayoutAttributeTop multiplier:1.0f constant:topMarginForAlert]];
  1145. [presentedViewConstraints addObject:[NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationLessThanOrEqual toItem:self.containerView attribute:NSLayoutAttributeBottom multiplier:1.0f constant:-bottomMarginForAlert]];
  1146. [presentedViewConstraints addObject:[NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0]];
  1147. [presentedViewConstraints addObject:[NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:alertController.offsetYForAlert]];
  1148. }
  1149. [presentedViewConstraints addObject:[NSLayoutConstraint constraintWithItem:presentedView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:alertH]];
  1150. }
  1151. [self.containerView addConstraints:presentedViewConstraints];
  1152. self.presentedViewConstraints = presentedViewConstraints;
  1153. }
  1154. - (void)presentationTransitionWillBegin {
  1155. [super presentationTransitionWillBegin];
  1156. UIView *overlayView = [[UIView alloc] init];
  1157. overlayView.frame = self.containerView.bounds;
  1158. overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  1159. overlayView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
  1160. overlayView.alpha = 0;
  1161. [self.containerView addSubview:overlayView];
  1162. _overlayView = overlayView;
  1163. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapOverlayView)];
  1164. [overlayView addGestureRecognizer:tap];
  1165. SPAlertController *alertController = (SPAlertController *)self.presentedViewController;
  1166. for (int i = 0; i < alertController.textFields.count; i++) {
  1167. UITextField *textField = alertController.textFields[i];
  1168. [alertController.textFieldView addSubview:textField];
  1169. if (i == 0) {
  1170. [textField becomeFirstResponder];
  1171. }
  1172. }
  1173. [alertController layoutViewConstraints];
  1174. }
  1175. - (void)presentationTransitionDidEnd:(BOOL)completed {
  1176. [super presentationTransitionDidEnd:completed];
  1177. }
  1178. - (void)dismissalTransitionWillBegin {
  1179. [super dismissalTransitionWillBegin];
  1180. }
  1181. - (void)dismissalTransitionDidEnd:(BOOL)completed {
  1182. [super dismissalTransitionDidEnd:completed];
  1183. if (completed) {
  1184. [_overlayView removeFromSuperview];
  1185. _overlayView = nil;
  1186. }
  1187. }
  1188. - (CGRect)frameOfPresentedViewInContainerView{
  1189. return self.presentedView.frame;
  1190. }
  1191. - (void)tapOverlayView {
  1192. [self.presentedViewController dismissViewControllerAnimated:YES completion:^{}];
  1193. }
  1194. @end
  1195. #pragma mark ---------------------------- SPAlertPresentationController end --------------------------------
  1196. #pragma mark ---------------------------- SPAlertAnimation begin --------------------------------
  1197. @interface SPAlertAnimation()
  1198. @property (nonatomic, assign) BOOL presenting;
  1199. @end
  1200. @implementation SPAlertAnimation
  1201. - (instancetype)initWithPresenting:(BOOL)isPresenting {
  1202. if (self = [super init]) {
  1203. self.presenting = isPresenting;
  1204. }
  1205. return self;
  1206. }
  1207. + (instancetype)animationIsPresenting:(BOOL)isPresenting {
  1208. return [[self alloc] initWithPresenting:isPresenting];
  1209. }
  1210. #pragma mark - UIViewControllerAnimatedTransitioning
  1211. - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
  1212. return 0.25f;
  1213. }
  1214. - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
  1215. if (self.presenting) {
  1216. [self presentAnimationTransition:transitionContext];
  1217. } else {
  1218. [self dismissAnimationTransition:transitionContext];
  1219. }
  1220. }
  1221. - (void)presentAnimationTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
  1222. SPAlertController *alertController = (SPAlertController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
  1223. CGSize controlViewSize = alertController.view.bounds.size;
  1224. // 获取presentationController,注意不是presentedController
  1225. SPAlertPresentationController *presentedController = (SPAlertPresentationController *)alertController.presentationController;
  1226. UIView *overlayView = presentedController.overlayView;
  1227. switch (alertController.animationType) {
  1228. case SPAlertAnimationTypeRaiseUp:
  1229. [self raiseUpWhenPresentForController:alertController
  1230. transition:transitionContext
  1231. controlViewSize:controlViewSize
  1232. overlayView:overlayView];
  1233. break;
  1234. case SPAlertAnimationTypeDropDown:
  1235. [self dropDownWhenPresentForController:alertController
  1236. transition:transitionContext
  1237. controlViewSize:controlViewSize
  1238. overlayView:overlayView];
  1239. break;
  1240. case SPAlertAnimationTypeAlpha:
  1241. [self alphaWhenPresentForController:alertController
  1242. transition:transitionContext
  1243. controlViewSize:controlViewSize
  1244. overlayView:overlayView];
  1245. break;
  1246. case SPAlertAnimationTypeExpand:
  1247. [self expandWhenPresentForController:alertController
  1248. transition:transitionContext
  1249. controlViewSize:controlViewSize
  1250. overlayView:overlayView];
  1251. break;
  1252. case SPAlertAnimationTypeShrink:
  1253. [self shrinkWhenPresentForController:alertController
  1254. transition:transitionContext
  1255. controlViewSize:controlViewSize
  1256. overlayView:overlayView];
  1257. break;
  1258. default:
  1259. break;
  1260. }
  1261. }
  1262. - (void)dismissAnimationTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
  1263. SPAlertController *alertController = (SPAlertController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
  1264. CGSize controlViewSize = alertController.view.bounds.size;
  1265. // 获取presentationController,注意不是presentedController
  1266. SPAlertPresentationController *presentedController = (SPAlertPresentationController *)alertController.presentationController;
  1267. UIView *overlayView = presentedController.overlayView;
  1268. switch (alertController.animationType) {
  1269. case SPAlertAnimationTypeRaiseUp:
  1270. [self dismissCorrespondingRaiseUpForController:alertController
  1271. transition:transitionContext
  1272. controlViewSize:controlViewSize
  1273. overlayView:overlayView];
  1274. break;
  1275. case SPAlertAnimationTypeDropDown:
  1276. [self dismissCorrespondingDropDownForController:alertController
  1277. transition:transitionContext
  1278. controlViewSize:controlViewSize
  1279. overlayView:overlayView];
  1280. break;
  1281. case SPAlertAnimationTypeAlpha:
  1282. [self dismissCorrespondingAlphaForController:alertController
  1283. transition:transitionContext
  1284. controlViewSize:controlViewSize
  1285. overlayView:overlayView];
  1286. break;
  1287. case SPAlertAnimationTypeExpand:
  1288. [self dismissCorrespondingExpandForController:alertController
  1289. transition:transitionContext
  1290. controlViewSize:controlViewSize
  1291. overlayView:overlayView];
  1292. break;
  1293. case SPAlertAnimationTypeShrink:
  1294. [self dismissCorrespondingShrinkForController:alertController
  1295. transition:transitionContext
  1296. controlViewSize:controlViewSize
  1297. overlayView:overlayView];
  1298. break;
  1299. default:
  1300. break;
  1301. }
  1302. }
  1303. // 从底部忘上弹的present动画
  1304. - (void)raiseUpWhenPresentForController:(SPAlertController *)alertController
  1305. transition:(id<UIViewControllerContextTransitioning>)transitionContext
  1306. controlViewSize:(CGSize)controlViewSize overlayView:(UIView *)overlayView {
  1307. CGRect controlViewFrame = alertController.view.frame;
  1308. controlViewFrame.origin.y = ScreenHeight;
  1309. alertController.view.frame = controlViewFrame;
  1310. UIView *containerView = [transitionContext containerView];
  1311. [containerView addSubview:alertController.view];
  1312. [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
  1313. CGRect controlViewFrame = alertController.view.frame;
  1314. controlViewFrame.origin.y = ScreenHeight-controlViewSize.height;
  1315. alertController.view.frame = controlViewFrame;
  1316. overlayView.alpha = 1.0;
  1317. } completion:^(BOOL finished) {
  1318. [transitionContext completeTransition:YES];
  1319. }];
  1320. }
  1321. // 从底部往上弹对应的dismiss动画
  1322. - (void)dismissCorrespondingRaiseUpForController:(SPAlertController *)alertController
  1323. transition:(id<UIViewControllerContextTransitioning>)transitionContext
  1324. controlViewSize:(CGSize)controlViewSize overlayView:(UIView *)overlayView {
  1325. [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
  1326. CGRect controlViewFrame = alertController.view.frame;
  1327. controlViewFrame.origin.y = ScreenHeight;
  1328. alertController.view.frame = controlViewFrame;
  1329. overlayView.alpha = 0.0;
  1330. } completion:^(BOOL finished) {
  1331. [transitionContext completeTransition:YES];
  1332. }];
  1333. }
  1334. // 从顶部往下弹的present动画
  1335. - (void)dropDownWhenPresentForController:(SPAlertController *)alertController
  1336. transition:(id<UIViewControllerContextTransitioning>)transitionContext
  1337. controlViewSize:(CGSize)controlViewSize overlayView:(UIView *)overlayView {
  1338. CGRect controlViewFrame = alertController.view.frame;
  1339. controlViewFrame.origin.y = -controlViewSize.height;
  1340. alertController.view.frame = controlViewFrame;
  1341. UIView *containerView = [transitionContext containerView];
  1342. [containerView addSubview:alertController.view];
  1343. [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
  1344. CGRect controlViewFrame = alertController.view.frame;
  1345. controlViewFrame.origin.y = 0;
  1346. alertController.view.frame = controlViewFrame;
  1347. overlayView.alpha = 1.0;
  1348. } completion:^(BOOL finished) {
  1349. [transitionContext completeTransition:YES];
  1350. }];
  1351. }
  1352. // 从顶部往下弹对应的dismiss动画
  1353. - (void)dismissCorrespondingDropDownForController:(SPAlertController *)alertController
  1354. transition:(id<UIViewControllerContextTransitioning>)transitionContext
  1355. controlViewSize:(CGSize)controlViewSize overlayView:(UIView *)overlayView {
  1356. [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
  1357. CGRect controlViewFrame = alertController.view.frame;
  1358. controlViewFrame.origin.y = -controlViewSize.height;
  1359. alertController.view.frame = controlViewFrame;
  1360. overlayView.alpha = 0.0;
  1361. } completion:^(BOOL finished) {
  1362. [transitionContext completeTransition:YES];
  1363. }];
  1364. }
  1365. // alpha值从0到1变化的present动画
  1366. - (void)alphaWhenPresentForController:(SPAlertController *)alertController
  1367. transition:(id<UIViewControllerContextTransitioning>)transitionContext
  1368. controlViewSize:(CGSize)controlViewSize overlayView:(UIView *)overlayView {
  1369. alertController.view.alpha = 0;
  1370. UIView *containerView = [transitionContext containerView];
  1371. [containerView addSubview:alertController.view];
  1372. [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
  1373. alertController.view.alpha = 1.0;
  1374. overlayView.alpha = 1.0;
  1375. } completion:^(BOOL finished) {
  1376. [transitionContext completeTransition:YES];
  1377. }];
  1378. }
  1379. // alpha值从0到1变化对应的的dismiss动画
  1380. - (void)dismissCorrespondingAlphaForController:(SPAlertController *)alertController
  1381. transition:(id<UIViewControllerContextTransitioning>)transitionContext
  1382. controlViewSize:(CGSize)controlViewSize overlayView:(UIView *)overlayView {
  1383. UIView *containerView = [transitionContext containerView];
  1384. [containerView addSubview:alertController.view];
  1385. [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
  1386. alertController.view.alpha = 0;
  1387. overlayView.alpha = 0.0;
  1388. } completion:^(BOOL finished) {
  1389. [transitionContext completeTransition:YES];
  1390. }];
  1391. }
  1392. // 发散的prensent动画
  1393. - (void)expandWhenPresentForController:(SPAlertController *)alertController
  1394. transition:(id<UIViewControllerContextTransitioning>)transitionContext
  1395. controlViewSize:(CGSize)controlViewSize overlayView:(UIView *)overlayView {
  1396. alertController.view.transform = CGAffineTransformMakeScale(0.5, 0.5);
  1397. UIView *containerView = [transitionContext containerView];
  1398. [containerView addSubview:alertController.view];
  1399. [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 usingSpringWithDamping:0.6 initialSpringVelocity:20 options:UIViewAnimationOptionCurveLinear animations:^{
  1400. alertController.view.transform = CGAffineTransformIdentity;
  1401. overlayView.alpha = 1.0;
  1402. } completion:^(BOOL finished) {
  1403. [transitionContext completeTransition:YES];
  1404. }];
  1405. }
  1406. // 发散对应的dismiss动画
  1407. - (void)dismissCorrespondingExpandForController:(SPAlertController *)alertController
  1408. transition:(id<UIViewControllerContextTransitioning>)transitionContext
  1409. controlViewSize:(CGSize)controlViewSize overlayView:(UIView *)overlayView {
  1410. UIView *containerView = [transitionContext containerView];
  1411. [containerView addSubview:alertController.view];
  1412. [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
  1413. alertController.view.transform = CGAffineTransformMakeScale(0, 0);
  1414. overlayView.alpha = 0.0;
  1415. } completion:^(BOOL finished) {
  1416. [transitionContext completeTransition:YES];
  1417. }];
  1418. }
  1419. // 收缩的present动画
  1420. - (void)shrinkWhenPresentForController:(SPAlertController *)alertController
  1421. transition:(id<UIViewControllerContextTransitioning>)transitionContext
  1422. controlViewSize:(CGSize)controlViewSize overlayView:(UIView *)overlayView {
  1423. alertController.view.transform = CGAffineTransformMakeScale(1.1, 1.1);
  1424. UIView *containerView = [transitionContext containerView];
  1425. [containerView addSubview:alertController.view];
  1426. [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
  1427. alertController.view.transform = CGAffineTransformIdentity;
  1428. overlayView.alpha = 1.0;
  1429. } completion:^(BOOL finished) {
  1430. [transitionContext completeTransition:YES];
  1431. }];
  1432. }
  1433. // 收缩对应的的dismiss动画
  1434. - (void)dismissCorrespondingShrinkForController:(SPAlertController *)alertController
  1435. transition:(id<UIViewControllerContextTransitioning>)transitionContext
  1436. controlViewSize:(CGSize)controlViewSize overlayView:(UIView *)overlayView {
  1437. // 与发散对应的dismiss动画相同
  1438. [self dismissCorrespondingExpandForController:alertController transition:transitionContext controlViewSize:controlViewSize overlayView:overlayView ];
  1439. }
  1440. @end
  1441. #pragma mark ---------------------------- SPAlertAnimation end --------------------------------