LCActionSheet.m 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935
  1. //
  2. // LCActionSheet.m
  3. // LCActionSheet
  4. //
  5. // Created by Leo on 2015/4/27.
  6. //
  7. // Copyright (c) 2015-2017 Leo <leodaxia@gmail.com>
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining a copy
  10. // of this software and associated documentation files (the "Software"), to deal
  11. // in the Software without restriction, including without limitation the rights
  12. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. // copies of the Software, and to permit persons to whom the Software is
  14. // furnished to do so, subject to the following conditions:
  15. //
  16. // The above copyright notice and this permission notice shall be included in all
  17. // copies or substantial portions of the Software.
  18. //
  19. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  25. // SOFTWARE.
  26. #import "LCActionSheet.h"
  27. #import "LCActionSheetCell.h"
  28. #import "LCActionSheetViewController.h"
  29. #import "UIImage+LCActionSheet.h"
  30. #import "Masonry.h"
  31. #import "MH_MODIFY_LC.h"
  32. /// 取消按钮顶部的间隙 : 系统默认:5.0f Custom By RQ :10.0f
  33. static CGFloat const LCCancelButtonTopMargin = 10.0f;
  34. @interface LCActionSheet () <UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate>
  35. @property (nonatomic, strong) NSArray<NSString *> *otherButtonTitles;
  36. @property (nonatomic, assign) CGSize titleTextSize;
  37. @property (nonatomic, weak) UIView *bottomView;
  38. @property (nonatomic, weak) UIVisualEffectView *blurEffectView;
  39. @property (nonatomic, weak) UIView *darkView;
  40. @property (nonatomic, weak) UILabel *titleLabel;
  41. @property (nonatomic, weak) UITableView *tableView;
  42. @property (nonatomic, weak) UIView *divisionView;
  43. @property (nonatomic, weak) UIButton *cancelButton;
  44. @property (nonatomic, weak) UIView *whiteBgView;
  45. @property (nonatomic, weak) UIView *lineView;
  46. @property (nullable, nonatomic, strong) UIWindow *window;
  47. @end
  48. @implementation LCActionSheet
  49. - (void)dealloc {
  50. [[NSNotificationCenter defaultCenter] removeObserver:self];
  51. }
  52. + (instancetype)sheetWithTitle:(NSString *)title delegate:(id<LCActionSheetDelegate>)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... {
  53. id eachObject;
  54. va_list argumentList;
  55. NSMutableArray *tempOtherButtonTitles = nil;
  56. if (otherButtonTitles) {
  57. tempOtherButtonTitles = [[NSMutableArray alloc] initWithObjects:otherButtonTitles, nil];
  58. va_start(argumentList, otherButtonTitles);
  59. while ((eachObject = va_arg(argumentList, id))) {
  60. [tempOtherButtonTitles addObject:eachObject];
  61. }
  62. va_end(argumentList);
  63. }
  64. return [[self alloc] initWithTitle:title
  65. delegate:delegate
  66. cancelButtonTitle:cancelButtonTitle
  67. otherButtonTitleArray:tempOtherButtonTitles];
  68. }
  69. + (instancetype)sheetWithTitle:(NSString *)title cancelButtonTitle:(NSString *)cancelButtonTitle clicked:(LCActionSheetClickedHandler)clickedHandler otherButtonTitles:(NSString *)otherButtonTitles, ... {
  70. id eachObject;
  71. va_list argumentList;
  72. NSMutableArray *tempOtherButtonTitles = nil;
  73. if (otherButtonTitles) {
  74. tempOtherButtonTitles = [[NSMutableArray alloc] initWithObjects:otherButtonTitles, nil];
  75. va_start(argumentList, otherButtonTitles);
  76. while ((eachObject = va_arg(argumentList, id))) {
  77. [tempOtherButtonTitles addObject:eachObject];
  78. }
  79. va_end(argumentList);
  80. }
  81. return [[self alloc] initWithTitle:title
  82. cancelButtonTitle:cancelButtonTitle
  83. clicked:clickedHandler
  84. otherButtonTitleArray:tempOtherButtonTitles];
  85. }
  86. + (instancetype)sheetWithTitle:(NSString *)title cancelButtonTitle:(NSString *)cancelButtonTitle didDismiss:(LCActionSheetDidDismissHandler)didDismissHandler otherButtonTitles:(NSString *)otherButtonTitles, ... {
  87. id eachObject;
  88. va_list argumentList;
  89. NSMutableArray *tempOtherButtonTitles = nil;
  90. if (otherButtonTitles) {
  91. tempOtherButtonTitles = [[NSMutableArray alloc] initWithObjects:otherButtonTitles, nil];
  92. va_start(argumentList, otherButtonTitles);
  93. while ((eachObject = va_arg(argumentList, id))) {
  94. [tempOtherButtonTitles addObject:eachObject];
  95. }
  96. va_end(argumentList);
  97. }
  98. return [[self alloc] initWithTitle:title
  99. cancelButtonTitle:cancelButtonTitle
  100. didDismiss:didDismissHandler
  101. otherButtonTitleArray:tempOtherButtonTitles];
  102. }
  103. + (instancetype)sheetWithTitle:(NSString *)title delegate:(id<LCActionSheetDelegate>)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitleArray:(NSArray<NSString *> *)otherButtonTitleArray {
  104. return [[self alloc] initWithTitle:title
  105. delegate:delegate
  106. cancelButtonTitle:cancelButtonTitle
  107. otherButtonTitleArray:otherButtonTitleArray];
  108. }
  109. + (instancetype)sheetWithTitle:(NSString *)title cancelButtonTitle:(NSString *)cancelButtonTitle clicked:(LCActionSheetClickedHandler)clickedHandler otherButtonTitleArray:(NSArray<NSString *> *)otherButtonTitleArray {
  110. return [[self alloc] initWithTitle:title
  111. cancelButtonTitle:cancelButtonTitle
  112. clicked:clickedHandler
  113. otherButtonTitleArray:otherButtonTitleArray];
  114. }
  115. + (instancetype)sheetWithTitle:(NSString *)title cancelButtonTitle:(nullable NSString *)cancelButtonTitle didDismiss:(nullable LCActionSheetDidDismissHandler)didDismissHandler otherButtonTitleArray:(nullable NSArray<NSString *> *)otherButtonTitleArray {
  116. return [[self alloc] initWithTitle:title
  117. cancelButtonTitle:cancelButtonTitle
  118. didDismiss:didDismissHandler
  119. otherButtonTitleArray:otherButtonTitleArray];
  120. }
  121. - (instancetype)initWithTitle:(NSString *)title delegate:(id<LCActionSheetDelegate>)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... {
  122. if (self = [super init]) {
  123. [self config:LCActionSheetConfig.config];
  124. id eachObject;
  125. va_list argumentList;
  126. NSMutableArray *tempOtherButtonTitles = nil;
  127. if (otherButtonTitles) {
  128. tempOtherButtonTitles = [[NSMutableArray alloc] initWithObjects:otherButtonTitles, nil];
  129. va_start(argumentList, otherButtonTitles);
  130. while ((eachObject = va_arg(argumentList, id))) {
  131. [tempOtherButtonTitles addObject:eachObject];
  132. }
  133. va_end(argumentList);
  134. }
  135. self.title = title;
  136. self.delegate = delegate;
  137. self.cancelButtonTitle = cancelButtonTitle;
  138. self.otherButtonTitles = tempOtherButtonTitles;
  139. [self setupView];
  140. }
  141. return self;
  142. }
  143. - (instancetype)initWithTitle:(NSString *)title cancelButtonTitle:(NSString *)cancelButtonTitle clicked:(LCActionSheetClickedHandler)clickedHandler otherButtonTitles:(NSString *)otherButtonTitles, ... {
  144. if (self = [super init]) {
  145. [self config:LCActionSheetConfig.config];
  146. id eachObject;
  147. va_list argumentList;
  148. NSMutableArray *tempOtherButtonTitles = nil;
  149. if (otherButtonTitles) {
  150. tempOtherButtonTitles = [[NSMutableArray alloc] initWithObjects:otherButtonTitles, nil];
  151. va_start(argumentList, otherButtonTitles);
  152. while ((eachObject = va_arg(argumentList, id))) {
  153. [tempOtherButtonTitles addObject:eachObject];
  154. }
  155. va_end(argumentList);
  156. }
  157. self.title = title;
  158. self.cancelButtonTitle = cancelButtonTitle;
  159. self.clickedHandler = clickedHandler;
  160. self.otherButtonTitles = tempOtherButtonTitles;
  161. [self setupView];
  162. }
  163. return self;
  164. }
  165. - (instancetype)initWithTitle:(NSString *)title cancelButtonTitle:(NSString *)cancelButtonTitle didDismiss:(LCActionSheetDidDismissHandler)didDismissHandler otherButtonTitles:(NSString *)otherButtonTitles, ... {
  166. if (self = [super init]) {
  167. [self config:LCActionSheetConfig.config];
  168. id eachObject;
  169. va_list argumentList;
  170. NSMutableArray *tempOtherButtonTitles = nil;
  171. if (otherButtonTitles) {
  172. tempOtherButtonTitles = [[NSMutableArray alloc] initWithObjects:otherButtonTitles, nil];
  173. va_start(argumentList, otherButtonTitles);
  174. while ((eachObject = va_arg(argumentList, id))) {
  175. [tempOtherButtonTitles addObject:eachObject];
  176. }
  177. va_end(argumentList);
  178. }
  179. self.title = title;
  180. self.cancelButtonTitle = cancelButtonTitle;
  181. self.didDismissHandler = didDismissHandler;
  182. self.otherButtonTitles = tempOtherButtonTitles;
  183. [self setupView];
  184. }
  185. return self;
  186. }
  187. - (instancetype)initWithTitle:(NSString *)title delegate:(id<LCActionSheetDelegate>)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitleArray:(NSArray<NSString *> *)otherButtonTitleArray {
  188. if (self = [super init]) {
  189. [self config:LCActionSheetConfig.config];
  190. self.title = title;
  191. self.delegate = delegate;
  192. self.cancelButtonTitle = cancelButtonTitle;
  193. self.otherButtonTitles = otherButtonTitleArray;
  194. [self setupView];
  195. }
  196. return self;
  197. }
  198. - (instancetype)initWithTitle:(NSString *)title cancelButtonTitle:(NSString *)cancelButtonTitle clicked:(LCActionSheetClickedHandler)clickedHandler otherButtonTitleArray:(NSArray<NSString *> *)otherButtonTitleArray {
  199. if (self = [super init]) {
  200. [self config:LCActionSheetConfig.config];
  201. self.title = title;
  202. self.cancelButtonTitle = cancelButtonTitle;
  203. self.clickedHandler = clickedHandler;
  204. self.otherButtonTitles = otherButtonTitleArray;
  205. [self setupView];
  206. }
  207. return self;
  208. }
  209. - (instancetype)initWithTitle:(NSString *)title cancelButtonTitle:(NSString *)cancelButtonTitle didDismiss:(LCActionSheetDidDismissHandler)didDismissHandler otherButtonTitleArray:(NSArray<NSString *> *)otherButtonTitleArray {
  210. if (self = [super init]) {
  211. [self config:LCActionSheetConfig.config];
  212. self.title = title;
  213. self.cancelButtonTitle = cancelButtonTitle;
  214. self.didDismissHandler = didDismissHandler;
  215. self.otherButtonTitles = otherButtonTitleArray;
  216. [self setupView];
  217. }
  218. return self;
  219. }
  220. - (instancetype)config:(LCActionSheetConfig *)config {
  221. _title = config.title;
  222. _cancelButtonTitle = config.cancelButtonTitle;
  223. _destructiveButtonIndexSet = config.destructiveButtonIndexSet;
  224. _destructiveButtonColor = config.destructiveButtonColor;
  225. _titleColor = config.titleColor;
  226. _buttonColor = config.buttonColor;
  227. _titleFont = config.titleFont;
  228. _buttonFont = config.buttonFont;
  229. _buttonHeight = config.buttonHeight;
  230. _scrolling = config.canScrolling;
  231. _visibleButtonCount = config.visibleButtonCount;
  232. _animationDuration = config.animationDuration;
  233. _darkOpacity = config.darkOpacity;
  234. _darkViewNoTaped = config.darkViewNoTaped;
  235. _unBlur = config.unBlur;
  236. _blurEffectStyle = config.blurEffectStyle;
  237. _titleEdgeInsets = config.titleEdgeInsets;
  238. _separatorColor = config.separatorColor;
  239. _autoHideWhenDeviceRotated = config.autoHideWhenDeviceRotated;
  240. return self;
  241. }
  242. - (void)setupView {
  243. @weakify(self)
  244. NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
  245. [notificationCenter addObserver:self
  246. selector:@selector(handleDidChangeStatusBarOrientation)
  247. name:UIApplicationDidChangeStatusBarOrientationNotification
  248. object:nil];
  249. UIView *bottomView = [[UIView alloc] init];
  250. [self addSubview:bottomView];
  251. [bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
  252. @strongify(self)
  253. make.left.right.equalTo(self);
  254. CGFloat height =
  255. (self.title.length > 0 ? self.titleTextSize.height + 2.0f + (self.titleEdgeInsets.top + self.titleEdgeInsets.bottom) : 0) +
  256. (self.otherButtonTitles.count > 0 ? (self.canScrolling ? MIN(self.visibleButtonCount, self.otherButtonTitles.count) : self.otherButtonTitles.count) * self.buttonHeight : 0) +
  257. (self.cancelButtonTitle.length > 0 ? LCCancelButtonTopMargin + self.buttonHeight + RQ_APPLICATION_SAFEAREA_BOTTOM_HEIGHT: 0);
  258. make.height.equalTo(@(height));
  259. make.bottom.equalTo(self).offset(height);
  260. }];
  261. self.bottomView = bottomView;
  262. UIView *darkView = [[UIView alloc] init];
  263. darkView.alpha = 0;
  264. darkView.userInteractionEnabled = NO;
  265. #if USER_MODIFY_LC_BY_RQ
  266. darkView.backgroundColor = LC_ACTION_SHEET_COLOR(0, 0, 0);
  267. #else
  268. darkView.backgroundColor = LC_ACTION_SHEET_COLOR(46, 49, 50);
  269. #endif
  270. [self addSubview:darkView];
  271. [darkView mas_makeConstraints:^(MASConstraintMaker *make) {
  272. @strongify(self)
  273. make.left.right.equalTo(self);
  274. make.top.equalTo(self).priorityLow();
  275. make.bottom.equalTo(bottomView.mas_top);
  276. }];
  277. self.darkView = darkView;
  278. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self
  279. action:@selector(darkViewClicked)];
  280. [darkView addGestureRecognizer:tap];
  281. UIView *whiteBgView = [[UIView alloc] init];
  282. whiteBgView.backgroundColor = [UIColor whiteColor];
  283. [bottomView addSubview:whiteBgView];
  284. [whiteBgView mas_makeConstraints:^(MASConstraintMaker *make) {
  285. make.edges.equalTo(bottomView);
  286. }];
  287. self.whiteBgView = whiteBgView;
  288. if (!self.unBlur) {
  289. [self blurBottomBgView];
  290. } else {
  291. whiteBgView.hidden = NO;
  292. }
  293. UILabel *titleLabel = [[UILabel alloc] init];
  294. titleLabel.text = self.title;
  295. titleLabel.font = self.titleFont;
  296. titleLabel.numberOfLines = 0;
  297. titleLabel.textColor = self.titleColor;
  298. titleLabel.textAlignment = NSTextAlignmentCenter;
  299. [bottomView addSubview:titleLabel];
  300. [titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
  301. @strongify(self)
  302. make.top.equalTo(bottomView).offset(self.title.length > 0 ? self.titleEdgeInsets.top : 0);
  303. make.left.equalTo(bottomView).offset(self.titleEdgeInsets.left);
  304. make.right.equalTo(bottomView).offset(-self.titleEdgeInsets.right);
  305. CGFloat height = self.title.length > 0 ? self.titleTextSize.height + 2.0f : 0; // Prevent omit
  306. make.height.equalTo(@(height));
  307. }];
  308. self.titleLabel = titleLabel;
  309. UITableView *tableView = [[UITableView alloc] init];
  310. tableView.backgroundColor = [UIColor clearColor];
  311. tableView.dataSource = self;
  312. tableView.delegate = self;
  313. [bottomView addSubview:tableView];
  314. [tableView mas_makeConstraints:^(MASConstraintMaker *make) {
  315. @strongify(self)
  316. make.left.right.equalTo(bottomView);
  317. make.top.equalTo(titleLabel.mas_bottom).offset(self.title.length > 0 ? self.titleEdgeInsets.bottom : 0);
  318. CGFloat height = self.otherButtonTitles.count * self.buttonHeight;
  319. make.height.equalTo(@(height));
  320. }];
  321. tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  322. self.tableView = tableView;
  323. UIView *lineView = [[UIView alloc] init];
  324. lineView.backgroundColor = self.separatorColor;
  325. lineView.contentMode = UIViewContentModeBottom;
  326. lineView.clipsToBounds = YES;
  327. [bottomView addSubview:lineView];
  328. [lineView mas_makeConstraints:^(MASConstraintMaker *make) {
  329. make.left.right.equalTo(bottomView);
  330. make.bottom.equalTo(tableView.mas_top);
  331. make.height.equalTo(@0.5f);
  332. }];
  333. self.lineView = lineView;
  334. self.lineView.hidden = !self.title || self.title.length == 0;
  335. UIView *divisionView = [[UIView alloc] init];
  336. divisionView.alpha = 0.3f;
  337. divisionView.backgroundColor = self.separatorColor;
  338. [bottomView addSubview:divisionView];
  339. [divisionView mas_makeConstraints:^(MASConstraintMaker *make) {
  340. @strongify(self)
  341. make.left.right.equalTo(bottomView);
  342. make.top.equalTo(tableView.mas_bottom);
  343. CGFloat height = self.cancelButtonTitle.length > 0 ? LCCancelButtonTopMargin : 0;
  344. make.height.equalTo(@(height));
  345. }];
  346. self.divisionView = divisionView;
  347. UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeCustom];
  348. cancelButton.backgroundColor = [UIColor clearColor];
  349. cancelButton.titleLabel.font = self.buttonFont;
  350. [cancelButton setTitle:self.cancelButtonTitle forState:UIControlStateNormal];
  351. [cancelButton setTitleColor:self.buttonColor forState:UIControlStateNormal];
  352. #if USER_MODIFY_LC_BY_RQ
  353. /// fix 掉通过 LCActionSheetConfig 配置actionsheet的bug
  354. if ([self.destructiveButtonIndexSet containsIndex:0]) {
  355. [cancelButton setTitleColor:self.destructiveButtonColor forState:UIControlStateNormal];
  356. }
  357. #endif
  358. [cancelButton setBackgroundImage:[UIImage lc_imageWithColor:self.separatorColor]
  359. forState:UIControlStateHighlighted];
  360. [cancelButton addTarget:self
  361. action:@selector(cancelButtonClicked)
  362. forControlEvents:UIControlEventTouchUpInside];
  363. [bottomView addSubview:cancelButton];
  364. [cancelButton mas_makeConstraints:^(MASConstraintMaker *make) {
  365. @strongify(self)
  366. make.left.right.equalTo(bottomView);
  367. make.top.equalTo(divisionView.mas_bottom);
  368. CGFloat height = self.cancelButtonTitle.length > 0 ? self.buttonHeight : 0;
  369. make.height.equalTo(@(height));
  370. }];
  371. self.cancelButton = cancelButton;
  372. }
  373. - (void)appendButtonsWithTitles:(NSString *)titles, ... {
  374. id eachObject;
  375. va_list argumentList;
  376. NSMutableArray *tempButtonTitles = nil;
  377. if (titles) {
  378. tempButtonTitles = [[NSMutableArray alloc] initWithObjects:titles, nil];
  379. va_start(argumentList, titles);
  380. while ((eachObject = va_arg(argumentList, id))) {
  381. [tempButtonTitles addObject:eachObject];
  382. }
  383. va_end(argumentList);
  384. }
  385. self.otherButtonTitles = [self.otherButtonTitles arrayByAddingObjectsFromArray:tempButtonTitles];
  386. [self.tableView reloadData];
  387. [self updateBottomView];
  388. [self updateTableView];
  389. }
  390. - (void)appendButtonWithTitle:(NSString *)title atIndex:(NSInteger)index {
  391. #ifdef DEBUG
  392. NSAssert(index != 0, @"Index 0 is cancel button");
  393. NSAssert(index <= self.otherButtonTitles.count + 1, @"Index crossed");
  394. #endif
  395. NSMutableArray<NSString *> *arrayM = [NSMutableArray arrayWithArray:self.otherButtonTitles];
  396. [arrayM insertObject:title atIndex:index - 1];
  397. self.otherButtonTitles = [NSArray arrayWithArray:arrayM];
  398. [self.tableView reloadData];
  399. [self updateBottomView];
  400. [self updateTableView];
  401. }
  402. - (void)appendButtonsWithTitles:(NSArray<NSString *> *)titles atIndexes:(NSIndexSet *)indexes {
  403. #ifdef DEBUG
  404. NSAssert(titles.count == indexes.count, @"Count of titles differs from count of indexs");
  405. #endif
  406. NSMutableIndexSet *indexSetM = [[NSMutableIndexSet alloc] init];
  407. [indexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
  408. #ifdef DEBUG
  409. NSAssert(idx != 0, @"Index 0 is cancel button");
  410. NSAssert(idx <= self.otherButtonTitles.count + indexes.count, @"Index crossed");
  411. #endif
  412. [indexSetM addIndex:idx - 1];
  413. }];
  414. NSMutableArray<NSString *> *arrayM = [NSMutableArray arrayWithArray:self.otherButtonTitles];
  415. [arrayM insertObjects:titles atIndexes:indexSetM];
  416. self.otherButtonTitles = [NSArray arrayWithArray:arrayM];
  417. [self.tableView reloadData];
  418. [self updateBottomView];
  419. [self updateTableView];
  420. }
  421. - (void)handleDidChangeStatusBarOrientation {
  422. if (self.autoHideWhenDeviceRotated) {
  423. [self hideWithButtonIndex:self.cancelButtonIndex];
  424. }
  425. }
  426. - (void)blurBottomBgView {
  427. @weakify(self)
  428. self.whiteBgView.hidden = YES;
  429. if (!self.blurEffectView) {
  430. UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:self.blurEffectStyle];
  431. UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
  432. [self.bottomView addSubview:blurEffectView];
  433. [blurEffectView mas_makeConstraints:^(MASConstraintMaker *make) {
  434. @strongify(self)
  435. make.edges.equalTo(self.bottomView);
  436. }];
  437. self.blurEffectView = blurEffectView;
  438. [self.bottomView sendSubviewToBack:blurEffectView];
  439. }
  440. }
  441. - (void)unBlurBottomBgView {
  442. self.whiteBgView.hidden = NO;
  443. if (self.blurEffectView) {
  444. [self.blurEffectView removeFromSuperview];
  445. self.blurEffectView = nil;
  446. }
  447. }
  448. #pragma mark - Setter & Getter
  449. - (void)setTitle:(NSString *)title {
  450. @weakify(self)
  451. _title = [title copy];
  452. self.titleLabel.text = title;
  453. [self updateTitleLabel];
  454. [self.tableView mas_updateConstraints:^(MASConstraintMaker *make) {
  455. @strongify(self)
  456. make.top.equalTo(self.titleLabel.mas_bottom).offset(self.title.length > 0 ? self.titleEdgeInsets.bottom : 0);
  457. }];
  458. self.lineView.hidden = !self.title || self.title.length == 0;
  459. }
  460. - (void)setCancelButtonTitle:(NSString *)cancelButtonTitle {
  461. _cancelButtonTitle = [cancelButtonTitle copy];
  462. [self.cancelButton setTitle:cancelButtonTitle forState:UIControlStateNormal];
  463. [self updateCancelButton];
  464. }
  465. - (void)setDestructiveButtonIndexSet:(NSIndexSet *)destructiveButtonIndexSet {
  466. _destructiveButtonIndexSet = destructiveButtonIndexSet;
  467. if ([destructiveButtonIndexSet containsIndex:0]) {
  468. [self.cancelButton setTitleColor:self.destructiveButtonColor forState:UIControlStateNormal];
  469. } else {
  470. [self.cancelButton setTitleColor:self.buttonColor forState:UIControlStateNormal];
  471. }
  472. [self.tableView reloadData];
  473. }
  474. - (void)setRedButtonIndexSet:(NSIndexSet *)redButtonIndexSet {
  475. self.destructiveButtonIndexSet = redButtonIndexSet;
  476. }
  477. - (NSIndexSet *)redButtonIndexSet {
  478. return self.destructiveButtonIndexSet;
  479. }
  480. - (void)setUnBlur:(BOOL)unBlur {
  481. _unBlur = unBlur;
  482. if (unBlur) {
  483. [self unBlurBottomBgView];
  484. } else {
  485. [self blurBottomBgView];
  486. }
  487. }
  488. - (void)setBlurEffectStyle:(UIBlurEffectStyle)blurEffectStyle {
  489. _blurEffectStyle = blurEffectStyle;
  490. [self unBlurBottomBgView];
  491. [self blurBottomBgView];
  492. }
  493. - (void)setTitleFont:(UIFont *)aTitleFont {
  494. _titleFont = aTitleFont;
  495. self.titleLabel.font = aTitleFont;
  496. [self updateBottomView];
  497. [self updateTitleLabel];
  498. }
  499. - (void)setButtonFont:(UIFont *)aButtonFont {
  500. _buttonFont = aButtonFont;
  501. self.cancelButton.titleLabel.font = aButtonFont;
  502. [self.tableView reloadData];
  503. }
  504. - (void)setDestructiveButtonColor:(UIColor *)aDestructiveButtonColor {
  505. _destructiveButtonColor = aDestructiveButtonColor;
  506. if ([self.destructiveButtonIndexSet containsIndex:0]) {
  507. [self.cancelButton setTitleColor:self.destructiveButtonColor forState:UIControlStateNormal];
  508. } else {
  509. [self.cancelButton setTitleColor:self.buttonColor forState:UIControlStateNormal];
  510. }
  511. [self.tableView reloadData];
  512. }
  513. - (void)setTitleColor:(UIColor *)aTitleColor {
  514. _titleColor = aTitleColor;
  515. self.titleLabel.textColor = aTitleColor;
  516. }
  517. - (void)setButtonColor:(UIColor *)aButtonColor {
  518. _buttonColor = aButtonColor;
  519. [self.cancelButton setTitleColor:aButtonColor forState:UIControlStateNormal];
  520. [self.tableView reloadData];
  521. }
  522. - (void)setButtonHeight:(CGFloat)aButtonHeight {
  523. _buttonHeight = aButtonHeight;
  524. [self.tableView reloadData];
  525. [self updateBottomView];
  526. [self updateTableView];
  527. [self updateCancelButton];
  528. }
  529. - (NSInteger)cancelButtonIndex {
  530. return 0;
  531. }
  532. - (void)setScrolling:(BOOL)scrolling {
  533. _scrolling = scrolling;
  534. [self updateBottomView];
  535. [self updateTableView];
  536. }
  537. - (void)setVisibleButtonCount:(CGFloat)visibleButtonCount {
  538. _visibleButtonCount = visibleButtonCount;
  539. [self updateBottomView];
  540. [self updateTableView];
  541. }
  542. - (void)setTitleEdgeInsets:(UIEdgeInsets)titleEdgeInsets {
  543. _titleEdgeInsets = titleEdgeInsets;
  544. [self updateBottomView];
  545. [self updateTitleLabel];
  546. [self updateTableView];
  547. }
  548. - (void)setSeparatorColor:(UIColor *)separatorColor {
  549. _separatorColor = separatorColor;
  550. self.lineView.backgroundColor = separatorColor;
  551. self.divisionView.backgroundColor = separatorColor;
  552. [self.cancelButton setBackgroundImage:[UIImage lc_imageWithColor:separatorColor]
  553. forState:UIControlStateHighlighted];
  554. [self.tableView reloadData];
  555. }
  556. - (CGSize)titleTextSize {
  557. CGSize size = CGSizeMake([UIScreen mainScreen].bounds.size.width -
  558. (self.titleEdgeInsets.left + self.titleEdgeInsets.right),
  559. MAXFLOAT);
  560. NSStringDrawingOptions opts =
  561. NSStringDrawingUsesLineFragmentOrigin |
  562. NSStringDrawingUsesFontLeading;
  563. NSDictionary *attrs = @{NSFontAttributeName : self.titleFont};
  564. _titleTextSize =
  565. [self.title boundingRectWithSize:size
  566. options:opts
  567. attributes:attrs
  568. context:nil].size;
  569. return _titleTextSize;
  570. }
  571. #pragma mark - Update Views
  572. - (void)updateBottomView {
  573. @weakify(self)
  574. [self.bottomView mas_updateConstraints:^(MASConstraintMaker *make) {
  575. @strongify(self)
  576. CGFloat height = (self.title.length > 0 ? self.titleTextSize.height + 2.0f + (self.titleEdgeInsets.top + self.titleEdgeInsets.bottom) : 0) + (self.otherButtonTitles.count > 0 ? (self.canScrolling ? MIN(self.visibleButtonCount, self.otherButtonTitles.count) : self.otherButtonTitles.count) * self.buttonHeight : 0) + (self.cancelButtonTitle.length > 0 ? LCCancelButtonTopMargin + self.buttonHeight : 0);
  577. make.height.equalTo(@(height));
  578. }];
  579. }
  580. - (void)updateTitleLabel {
  581. @weakify(self)
  582. [self.titleLabel mas_updateConstraints:^(MASConstraintMaker *make) {
  583. @strongify(self)
  584. make.top.equalTo(self.bottomView).offset(self.title.length > 0 ? self.titleEdgeInsets.top : 0);
  585. make.left.equalTo(self.bottomView).offset(self.titleEdgeInsets.left);
  586. make.right.equalTo(self.bottomView).offset(-self.titleEdgeInsets.right);
  587. CGFloat height = self.title.length > 0 ? self.titleTextSize.height + 2.0f : 0; // Prevent omit
  588. make.height.equalTo(@(height));
  589. }];
  590. }
  591. - (void)updateTableView {
  592. @weakify(self)
  593. if (!self.canScrolling) {
  594. [self.tableView mas_updateConstraints:^(MASConstraintMaker *make) {
  595. @strongify(self)
  596. make.height.equalTo(@(self.otherButtonTitles.count * self.buttonHeight));
  597. make.top.equalTo(self.titleLabel.mas_bottom).offset(self.title.length > 0 ? self.titleEdgeInsets.bottom : 0);
  598. }];
  599. } else {
  600. [self.tableView mas_updateConstraints:^(MASConstraintMaker *make) {
  601. @strongify(self)
  602. make.height.equalTo(@(MIN(self.visibleButtonCount, self.otherButtonTitles.count) * self.buttonHeight));
  603. make.top.equalTo(self.titleLabel.mas_bottom).offset(self.title.length > 0 ? self.titleEdgeInsets.bottom : 0);
  604. }];
  605. }
  606. }
  607. - (void)updateCancelButton {
  608. @weakify(self)
  609. [self.divisionView mas_updateConstraints:^(MASConstraintMaker *make) {
  610. @strongify(self)
  611. CGFloat height = self.cancelButtonTitle.length > 0 ? LCCancelButtonTopMargin : 0;
  612. make.height.equalTo(@(height));
  613. }];
  614. [self.cancelButton mas_updateConstraints:^(MASConstraintMaker *make) {
  615. @strongify(self)
  616. CGFloat height = self.cancelButtonTitle.length > 0 ? self.buttonHeight : 0;
  617. make.height.equalTo(@(height));
  618. }];
  619. }
  620. #pragma mark - Show & Hide
  621. - (void)show {
  622. if ([self.delegate respondsToSelector:@selector(willPresentActionSheet:)]) {
  623. [self.delegate willPresentActionSheet:self];
  624. }
  625. if (self.willPresentHandler) {
  626. self.willPresentHandler(self);
  627. }
  628. LCActionSheetViewController *viewController = [[LCActionSheetViewController alloc] init];
  629. viewController.statusBarStyle = [UIApplication sharedApplication].statusBarStyle;
  630. UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  631. window.windowLevel = UIWindowLevelStatusBar;
  632. window.rootViewController = viewController;
  633. [window makeKeyAndVisible];
  634. self.window = window;
  635. [viewController.view addSubview:self];
  636. [self mas_makeConstraints:^(MASConstraintMaker *make) {
  637. make.edges.equalTo(viewController.view);
  638. }];
  639. [self.window layoutIfNeeded];
  640. __weak typeof(self) weakSelf = self;
  641. [UIView animateWithDuration:self.animationDuration delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
  642. __strong typeof(weakSelf) strongSelf = weakSelf;
  643. strongSelf.darkView.alpha = strongSelf.darkOpacity;
  644. strongSelf.darkView.userInteractionEnabled = !strongSelf.darkViewNoTaped;
  645. [strongSelf.bottomView mas_updateConstraints:^(MASConstraintMaker *make) {
  646. make.bottom.equalTo(strongSelf);
  647. }];
  648. [strongSelf layoutIfNeeded];
  649. } completion:^(BOOL finished) {
  650. __strong typeof(weakSelf) strongSelf = weakSelf;
  651. if ([strongSelf.delegate respondsToSelector:@selector(didPresentActionSheet:)]) {
  652. [strongSelf.delegate didPresentActionSheet:strongSelf];
  653. }
  654. if (strongSelf.didPresentHandler) {
  655. strongSelf.didPresentHandler(strongSelf);
  656. }
  657. }];
  658. }
  659. - (void)hideWithButtonIndex:(NSInteger)buttonIndex {
  660. if ([self.delegate respondsToSelector:@selector(actionSheet:willDismissWithButtonIndex:)]) {
  661. [self.delegate actionSheet:self willDismissWithButtonIndex:buttonIndex];
  662. }
  663. if (self.willDismissHandler) {
  664. self.willDismissHandler(self, buttonIndex);
  665. }
  666. __weak typeof(self) weakSelf = self;
  667. [UIView animateWithDuration:self.animationDuration delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
  668. __strong typeof(weakSelf) strongSelf = weakSelf;
  669. strongSelf.darkView.alpha = 0;
  670. strongSelf.darkView.userInteractionEnabled = NO;
  671. [strongSelf.bottomView mas_updateConstraints:^(MASConstraintMaker *make) {
  672. CGFloat height = (strongSelf.title.length > 0 ? strongSelf.titleTextSize.height + 2.0f + (strongSelf.titleEdgeInsets.top + strongSelf.titleEdgeInsets.bottom) : 0) + (strongSelf.otherButtonTitles.count > 0 ? (strongSelf.canScrolling ? MIN(strongSelf.visibleButtonCount, strongSelf.otherButtonTitles.count) : strongSelf.otherButtonTitles.count) * strongSelf.buttonHeight : 0) + (strongSelf.cancelButtonTitle.length > 0 ? LCCancelButtonTopMargin + strongSelf.buttonHeight : 0);
  673. make.bottom.equalTo(strongSelf).offset(height);
  674. }];
  675. [strongSelf layoutIfNeeded];
  676. } completion:^(BOOL finished) {
  677. __strong typeof(weakSelf) strongSelf = weakSelf;
  678. [strongSelf removeFromSuperview];
  679. strongSelf.window.rootViewController = nil;
  680. strongSelf.window.hidden = YES;
  681. strongSelf.window = nil;
  682. if ([strongSelf.delegate respondsToSelector:@selector(actionSheet:didDismissWithButtonIndex:)]) {
  683. [strongSelf.delegate actionSheet:strongSelf didDismissWithButtonIndex:buttonIndex];
  684. }
  685. if (strongSelf.didDismissHandler) {
  686. strongSelf.didDismissHandler(strongSelf, buttonIndex);
  687. }
  688. }];
  689. }
  690. - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
  691. if (!self.canScrolling) {
  692. scrollView.contentOffset = CGPointMake(0, 0);
  693. }
  694. }
  695. #pragma mark - LCActionSheet & UITableView Delegate
  696. - (void)darkViewClicked {
  697. [self cancelButtonClicked];
  698. }
  699. - (void)cancelButtonClicked {
  700. if ([self.delegate respondsToSelector:@selector(actionSheet:clickedButtonAtIndex:)]) {
  701. [self.delegate actionSheet:self clickedButtonAtIndex:0];
  702. }
  703. if (self.clickedHandler) {
  704. self.clickedHandler(self, 0);
  705. }
  706. [self hideWithButtonIndex:0];
  707. }
  708. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  709. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  710. if ([self.delegate respondsToSelector:@selector(actionSheet:clickedButtonAtIndex:)]) {
  711. [self.delegate actionSheet:self clickedButtonAtIndex:indexPath.row + 1];
  712. }
  713. if (self.clickedHandler) {
  714. self.clickedHandler(self, indexPath.row + 1);
  715. }
  716. [self hideWithButtonIndex:indexPath.row + 1];
  717. }
  718. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  719. return self.otherButtonTitles.count;
  720. }
  721. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  722. static NSString *cellID = @"LCActionSheetCell";
  723. LCActionSheetCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
  724. if (!cell) {
  725. cell = [[LCActionSheetCell alloc] initWithStyle:UITableViewCellStyleDefault
  726. reuseIdentifier:cellID];
  727. }
  728. cell.titleLabel.font = self.buttonFont;
  729. cell.titleLabel.textColor = self.buttonColor;
  730. cell.titleLabel.text = self.otherButtonTitles[indexPath.row];
  731. cell.cellSeparatorColor = self.separatorColor;
  732. // cell.lineView.hidden = indexPath.row == MAX(self.otherButtonTitles.count - 1, 0);
  733. if (indexPath.row == MAX(self.otherButtonTitles.count - 1, 0)) {
  734. cell.tag = LC_ACTION_SHEET_CELL_HIDDE_LINE_TAG;
  735. } else {
  736. cell.tag = LC_ACTION_SHEET_CELL_NO_HIDDE_LINE_TAG;
  737. }
  738. if (self.destructiveButtonIndexSet) {
  739. if ([self.destructiveButtonIndexSet containsIndex:indexPath.row + 1]) {
  740. cell.titleLabel.textColor = self.destructiveButtonColor;
  741. } else {
  742. cell.titleLabel.textColor = self.buttonColor;
  743. }
  744. }
  745. return cell;
  746. }
  747. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  748. return self.buttonHeight;
  749. }
  750. @end