JXMemberPromptViewController.m 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. //
  2. // JXMemberPromptViewController.m
  3. // AICity
  4. //
  5. // Feature: 003-ios-api-https
  6. // Task: T055 - iOS会员购买跳转
  7. // Created on 2025-10-14.
  8. //
  9. #import "JXMemberPromptViewController.h"
  10. #import "JXMemberService.h"
  11. @interface JXMemberPromptViewController ()
  12. // UI组件
  13. @property (nonatomic, strong) UIView *containerView;
  14. @property (nonatomic, strong) UIButton *closeButton;
  15. @property (nonatomic, strong) UIImageView *contentIcon;
  16. @property (nonatomic, strong) UILabel *titleLabel;
  17. @property (nonatomic, strong) UILabel *descriptionLabel;
  18. @property (nonatomic, strong) UILabel *currentStatusLabel;
  19. @property (nonatomic, strong) UILabel *requiredStatusLabel;
  20. @property (nonatomic, strong) UILabel *benefitsLabel;
  21. @property (nonatomic, strong) UIButton *purchaseButton;
  22. @property (nonatomic, strong) UIButton *trialButton;
  23. @property (nonatomic, strong) UIButton *cancelButton;
  24. // 服务
  25. @property (nonatomic, strong) JXMemberService *memberService;
  26. @end
  27. @implementation JXMemberPromptViewController
  28. - (void)viewDidLoad {
  29. [super viewDidLoad];
  30. self.view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.8];
  31. self.memberService = [JXMemberService sharedService];
  32. [self setupUI];
  33. [self updateUI];
  34. }
  35. - (void)setupUI {
  36. // 容器视图
  37. self.containerView = [[UIView alloc] init];
  38. self.containerView.backgroundColor = [UIColor whiteColor];
  39. self.containerView.layer.cornerRadius = 12.0;
  40. self.containerView.layer.masksToBounds = YES;
  41. [self.view addSubview:self.containerView];
  42. // 关闭按钮
  43. self.closeButton = [UIButton buttonWithType:UIButtonTypeSystem];
  44. [self.closeButton setTitle:@"✕" forState:UIControlStateNormal];
  45. self.closeButton.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightMedium];
  46. [self.closeButton setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
  47. [self.closeButton addTarget:self action:@selector(closeButtonTapped) forControlEvents:UIControlEventTouchUpInside];
  48. [self.containerView addSubview:self.closeButton];
  49. // 内容图标
  50. self.contentIcon = [[UIImageView alloc] init];
  51. self.contentIcon.image = [UIImage systemImageNamed:@"crown.fill"];
  52. self.contentIcon.tintColor = [UIColor systemOrangeColor];
  53. self.contentIcon.contentMode = UIViewContentModeScaleAspectFit;
  54. [self.containerView addSubview:self.contentIcon];
  55. // 标题
  56. self.titleLabel = [[UILabel alloc] init];
  57. self.titleLabel.font = [UIFont systemFontOfSize:20 weight:UIFontWeightBold];
  58. self.titleLabel.textColor = [UIColor blackColor];
  59. self.titleLabel.textAlignment = NSTextAlignmentCenter;
  60. self.titleLabel.numberOfLines = 0;
  61. [self.containerView addSubview:self.titleLabel];
  62. // 描述
  63. self.descriptionLabel = [[UILabel alloc] init];
  64. self.descriptionLabel.font = [UIFont systemFontOfSize:16];
  65. self.descriptionLabel.textColor = [UIColor darkGrayColor];
  66. self.descriptionLabel.textAlignment = NSTextAlignmentCenter;
  67. self.descriptionLabel.numberOfLines = 0;
  68. [self.containerView addSubview:self.descriptionLabel];
  69. // 当前状态
  70. self.currentStatusLabel = [[UILabel alloc] init];
  71. self.currentStatusLabel.font = [UIFont systemFontOfSize:14];
  72. self.currentStatusLabel.textColor = [UIColor grayColor];
  73. self.currentStatusLabel.textAlignment = NSTextAlignmentCenter;
  74. [self.containerView addSubview:self.currentStatusLabel];
  75. // 所需状态
  76. self.requiredStatusLabel = [[UILabel alloc] init];
  77. self.requiredStatusLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
  78. self.requiredStatusLabel.textColor = [UIColor systemOrangeColor];
  79. self.requiredStatusLabel.textAlignment = NSTextAlignmentCenter;
  80. [self.containerView addSubview:self.requiredStatusLabel];
  81. // 权益说明
  82. self.benefitsLabel = [[UILabel alloc] init];
  83. self.benefitsLabel.font = [UIFont systemFontOfSize:14];
  84. self.benefitsLabel.textColor = [UIColor darkGrayColor];
  85. self.benefitsLabel.numberOfLines = 0;
  86. [self.containerView addSubview:self.benefitsLabel];
  87. // 购买按钮
  88. self.purchaseButton = [UIButton buttonWithType:UIButtonTypeSystem];
  89. self.purchaseButton.backgroundColor = [UIColor systemOrangeColor];
  90. [self.purchaseButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  91. self.purchaseButton.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
  92. self.purchaseButton.layer.cornerRadius = 8.0;
  93. [self.purchaseButton addTarget:self action:@selector(purchaseButtonTapped) forControlEvents:UIControlEventTouchUpInside];
  94. [self.containerView addSubview:self.purchaseButton];
  95. // 试看按钮
  96. self.trialButton = [UIButton buttonWithType:UIButtonTypeSystem];
  97. [self.trialButton setTitle:@"免费试看3分钟" forState:UIControlStateNormal];
  98. [self.trialButton setTitleColor:[UIColor systemBlueColor] forState:UIControlStateNormal];
  99. self.trialButton.titleLabel.font = [UIFont systemFontOfSize:14];
  100. self.trialButton.layer.borderWidth = 1.0;
  101. self.trialButton.layer.borderColor = [UIColor systemBlueColor].CGColor;
  102. self.trialButton.layer.cornerRadius = 6.0;
  103. [self.trialButton addTarget:self action:@selector(trialButtonTapped) forControlEvents:UIControlEventTouchUpInside];
  104. [self.containerView addSubview:self.trialButton];
  105. // 取消按钮
  106. self.cancelButton = [UIButton buttonWithType:UIButtonTypeSystem];
  107. [self.cancelButton setTitle:@"取消" forState:UIControlStateNormal];
  108. [self.cancelButton setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
  109. self.cancelButton.titleLabel.font = [UIFont systemFontOfSize:16];
  110. [self.cancelButton addTarget:self action:@selector(cancelButtonTapped) forControlEvents:UIControlEventTouchUpInside];
  111. [self.containerView addSubview:self.cancelButton];
  112. [self setupConstraints];
  113. }
  114. - (void)setupConstraints {
  115. // 使用Auto Layout设置约束
  116. self.containerView.translatesAutoresizingMaskIntoConstraints = NO;
  117. self.closeButton.translatesAutoresizingMaskIntoConstraints = NO;
  118. self.contentIcon.translatesAutoresizingMaskIntoConstraints = NO;
  119. self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
  120. self.descriptionLabel.translatesAutoresizingMaskIntoConstraints = NO;
  121. self.currentStatusLabel.translatesAutoresizingMaskIntoConstraints = NO;
  122. self.requiredStatusLabel.translatesAutoresizingMaskIntoConstraints = NO;
  123. self.benefitsLabel.translatesAutoresizingMaskIntoConstraints = NO;
  124. self.purchaseButton.translatesAutoresizingMaskIntoConstraints = NO;
  125. self.trialButton.translatesAutoresizingMaskIntoConstraints = NO;
  126. self.cancelButton.translatesAutoresizingMaskIntoConstraints = NO;
  127. [NSLayoutConstraint activateConstraints:@[
  128. // 容器视图
  129. [self.containerView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
  130. [self.containerView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
  131. [self.containerView.widthAnchor constraintEqualToConstant:320],
  132. // 关闭按钮
  133. [self.closeButton.topAnchor constraintEqualToAnchor:self.containerView.topAnchor constant:16],
  134. [self.closeButton.trailingAnchor constraintEqualToAnchor:self.containerView.trailingAnchor constant:-16],
  135. [self.closeButton.widthAnchor constraintEqualToConstant:30],
  136. [self.closeButton.heightAnchor constraintEqualToConstant:30],
  137. // 内容图标
  138. [self.contentIcon.topAnchor constraintEqualToAnchor:self.containerView.topAnchor constant:24],
  139. [self.contentIcon.centerXAnchor constraintEqualToAnchor:self.containerView.centerXAnchor],
  140. [self.contentIcon.widthAnchor constraintEqualToConstant:48],
  141. [self.contentIcon.heightAnchor constraintEqualToConstant:48],
  142. // 标题
  143. [self.titleLabel.topAnchor constraintEqualToAnchor:self.contentIcon.bottomAnchor constant:16],
  144. [self.titleLabel.leadingAnchor constraintEqualToAnchor:self.containerView.leadingAnchor constant:20],
  145. [self.titleLabel.trailingAnchor constraintEqualToAnchor:self.containerView.trailingAnchor constant:-20],
  146. // 描述
  147. [self.descriptionLabel.topAnchor constraintEqualToAnchor:self.titleLabel.bottomAnchor constant:12],
  148. [self.descriptionLabel.leadingAnchor constraintEqualToAnchor:self.containerView.leadingAnchor constant:20],
  149. [self.descriptionLabel.trailingAnchor constraintEqualToAnchor:self.containerView.trailingAnchor constant:-20],
  150. // 当前状态
  151. [self.currentStatusLabel.topAnchor constraintEqualToAnchor:self.descriptionLabel.bottomAnchor constant:16],
  152. [self.currentStatusLabel.leadingAnchor constraintEqualToAnchor:self.containerView.leadingAnchor constant:20],
  153. [self.currentStatusLabel.trailingAnchor constraintEqualToAnchor:self.containerView.trailingAnchor constant:-20],
  154. // 所需状态
  155. [self.requiredStatusLabel.topAnchor constraintEqualToAnchor:self.currentStatusLabel.bottomAnchor constant:4],
  156. [self.requiredStatusLabel.leadingAnchor constraintEqualToAnchor:self.containerView.leadingAnchor constant:20],
  157. [self.requiredStatusLabel.trailingAnchor constraintEqualToAnchor:self.containerView.trailingAnchor constant:-20],
  158. // 权益说明
  159. [self.benefitsLabel.topAnchor constraintEqualToAnchor:self.requiredStatusLabel.bottomAnchor constant:16],
  160. [self.benefitsLabel.leadingAnchor constraintEqualToAnchor:self.containerView.leadingAnchor constant:20],
  161. [self.benefitsLabel.trailingAnchor constraintEqualToAnchor:self.containerView.trailingAnchor constant:-20],
  162. // 购买按钮
  163. [self.purchaseButton.topAnchor constraintEqualToAnchor:self.benefitsLabel.bottomAnchor constant:24],
  164. [self.purchaseButton.leadingAnchor constraintEqualToAnchor:self.containerView.leadingAnchor constant:20],
  165. [self.purchaseButton.trailingAnchor constraintEqualToAnchor:self.containerView.trailingAnchor constant:-20],
  166. [self.purchaseButton.heightAnchor constraintEqualToConstant:44],
  167. // 试看按钮
  168. [self.trialButton.topAnchor constraintEqualToAnchor:self.purchaseButton.bottomAnchor constant:12],
  169. [self.trialButton.leadingAnchor constraintEqualToAnchor:self.containerView.leadingAnchor constant:20],
  170. [self.trialButton.trailingAnchor constraintEqualToAnchor:self.containerView.trailingAnchor constant:-20],
  171. [self.trialButton.heightAnchor constraintEqualToConstant:36],
  172. // 取消按钮
  173. [self.cancelButton.topAnchor constraintEqualToAnchor:self.trialButton.bottomAnchor constant:12],
  174. [self.cancelButton.centerXAnchor constraintEqualToAnchor:self.containerView.centerXAnchor],
  175. [self.cancelButton.bottomAnchor constraintEqualToAnchor:self.containerView.bottomAnchor constant:-20],
  176. [self.cancelButton.heightAnchor constraintEqualToConstant:32]
  177. ]];
  178. }
  179. - (void)updateUI {
  180. // 更新标题和描述
  181. NSString *requiredMemberType = [self getRequiredMemberType:self.requiredLevel];
  182. self.titleLabel.text = [NSString stringWithFormat:@"观看此内容需要%@权限", [self getMemberTypeDisplayName:requiredMemberType]];
  183. switch (self.requiredLevel) {
  184. case 1:
  185. self.descriptionLabel.text = @"此为付费内容,需要开通会员才能观看完整视频";
  186. break;
  187. case 2:
  188. self.descriptionLabel.text = @"此为高级内容,需要高级会员或VIP会员权限";
  189. break;
  190. case 3:
  191. self.descriptionLabel.text = @"此为VIP专享内容,需要VIP会员权限";
  192. break;
  193. default:
  194. self.descriptionLabel.text = @"此内容需要会员权限才能观看";
  195. break;
  196. }
  197. // 更新当前状态
  198. self.currentStatusLabel.text = [NSString stringWithFormat:@"当前状态:%@", [self getMemberTypeDisplayName:self.memberStatus.localMemberType]];
  199. // 更新所需状态
  200. self.requiredStatusLabel.text = [NSString stringWithFormat:@"所需权限:%@", [self getMemberTypeDisplayName:requiredMemberType]];
  201. // 更新权益说明
  202. [self updateBenefitsText:requiredMemberType];
  203. // 更新按钮状态
  204. [self updateButtonStates:requiredMemberType];
  205. }
  206. - (void)updateBenefitsText:(NSString *)requiredMemberType {
  207. NSString *benefits;
  208. if ([requiredMemberType isEqualToString:@"basic"]) {
  209. benefits = @"• 观看所有付费内容\n• 无广告播放\n• 高清画质";
  210. } else if ([requiredMemberType isEqualToString:@"premium"]) {
  211. benefits = @"• 观看所有付费和高级内容\n• 无广告播放\n• 超高清画质\n• 离线下载";
  212. } else if ([requiredMemberType isEqualToString:@"vip"]) {
  213. benefits = @"• 观看所有内容包括VIP专享\n• 无广告播放\n• 4K超高清画质\n• 离线下载\n• 优先客服支持";
  214. } else {
  215. benefits = @"• 观看付费内容\n• 更好的观看体验";
  216. }
  217. self.benefitsLabel.text = benefits;
  218. }
  219. - (void)updateButtonStates:(NSString *)requiredMemberType {
  220. // 根据当前会员状态和所需权限更新按钮显示
  221. BOOL canUpgrade = [self canUpgradeToRequiredLevel:self.memberStatus.localMemberType requiredType:requiredMemberType];
  222. self.purchaseButton.enabled = canUpgrade;
  223. if ([self.memberStatus.localMemberType isEqualToString:@"free"]) {
  224. [self.purchaseButton setTitle:[NSString stringWithFormat:@"开通%@", [self getMemberTypeDisplayName:requiredMemberType]] forState:UIControlStateNormal];
  225. } else if (canUpgrade) {
  226. [self.purchaseButton setTitle:[NSString stringWithFormat:@"升级到%@", [self getMemberTypeDisplayName:requiredMemberType]] forState:UIControlStateNormal];
  227. } else {
  228. [self.purchaseButton setTitle:[NSString stringWithFormat:@"已是%@", [self getMemberTypeDisplayName:self.memberStatus.localMemberType]] forState:UIControlStateNormal];
  229. }
  230. // 试看按钮(某些内容可能支持试看)
  231. self.trialButton.hidden = ![self supportsTrial:self.requiredLevel];
  232. }
  233. #pragma mark - Button Actions
  234. - (void)closeButtonTapped {
  235. if (self.onCancel) {
  236. self.onCancel();
  237. }
  238. [self dismissViewControllerAnimated:YES completion:nil];
  239. }
  240. - (void)purchaseButtonTapped {
  241. [self navigateToMemberPurchase];
  242. }
  243. - (void)trialButtonTapped {
  244. if (self.onTrialPlay) {
  245. self.onTrialPlay(3 * 60); // 3分钟试看
  246. }
  247. [self dismissViewControllerAnimated:YES completion:nil];
  248. }
  249. - (void)cancelButtonTapped {
  250. if (self.onCancel) {
  251. self.onCancel();
  252. }
  253. [self dismissViewControllerAnimated:YES completion:nil];
  254. }
  255. - (void)navigateToMemberPurchase {
  256. // 跳转到本APP的会员购买页面
  257. // 这里需要根据实际的会员购买页面进行调整
  258. // 方式1: 使用URL Scheme跳转
  259. NSString *urlString = [NSString stringWithFormat:@"aicity://member/purchase?type=%@&source=juxing&episode=%@",
  260. [self getRequiredMemberType:self.requiredLevel], self.episodeId];
  261. NSURL *url = [NSURL URLWithString:urlString];
  262. if ([[UIApplication sharedApplication] canOpenURL:url]) {
  263. [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) {
  264. if (success) {
  265. // 跳转成功,监听会员购买结果
  266. [self startMonitoringMemberPurchase];
  267. } else {
  268. [self showFallbackPurchaseOptions];
  269. }
  270. }];
  271. } else {
  272. // 方式2: 发送通知跳转到主页面的会员购买Tab
  273. [[NSNotificationCenter defaultCenter] postNotificationName:@"ShowMemberPurchase"
  274. object:nil
  275. userInfo:@{
  276. @"required_type": [self getRequiredMemberType:self.requiredLevel],
  277. @"source": @"juxing_player",
  278. @"episode_id": self.episodeId ?: @""
  279. }];
  280. [self showFallbackPurchaseOptions];
  281. }
  282. }
  283. - (void)showFallbackPurchaseOptions {
  284. UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"开通会员"
  285. message:@"请前往主页面开通会员"
  286. preferredStyle:UIAlertControllerStyleAlert];
  287. [alert addAction:[UIAlertAction actionWithTitle:@"前往主页" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  288. // 跳转到主页面
  289. [[NSNotificationCenter defaultCenter] postNotificationName:@"NavigateToMainPage" object:nil];
  290. if (self.onCancel) {
  291. self.onCancel();
  292. }
  293. [self dismissViewControllerAnimated:YES completion:nil];
  294. }]];
  295. [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
  296. [self presentViewController:alert animated:YES completion:nil];
  297. }
  298. - (void)startMonitoringMemberPurchase {
  299. // 监听会员购买成功通知
  300. [[NSNotificationCenter defaultCenter] addObserver:self
  301. selector:@selector(memberPurchaseSuccessNotification:)
  302. name:@"MemberPurchaseSuccess"
  303. object:nil];
  304. // 设置超时监听(30秒后停止监听)
  305. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(30.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  306. [[NSNotificationCenter defaultCenter] removeObserver:self name:@"MemberPurchaseSuccess" object:nil];
  307. });
  308. }
  309. - (void)memberPurchaseSuccessNotification:(NSNotification *)notification {
  310. [[NSNotificationCenter defaultCenter] removeObserver:self name:@"MemberPurchaseSuccess" object:nil];
  311. // 刷新会员状态
  312. [self refreshMemberStatusAndPlay];
  313. }
  314. - (void)refreshMemberStatusAndPlay {
  315. [self.memberService getCurrentMemberStatusWithForceRefresh:YES completion:^(JXMemberStatus * _Nullable memberStatus, NSError * _Nullable error) {
  316. dispatch_async(dispatch_get_main_queue(), ^{
  317. if (error) {
  318. [self showAlert:@"获取会员状态失败" message:error.localizedDescription];
  319. return;
  320. }
  321. // 检查是否现在有权限
  322. if (memberStatus.accessLevel >= self.requiredLevel) {
  323. [self showAlert:@"会员开通成功!" message:@"即将开始播放"];
  324. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  325. if (self.onMemberPurchaseSuccess) {
  326. self.onMemberPurchaseSuccess();
  327. }
  328. [self dismissViewControllerAnimated:YES completion:nil];
  329. });
  330. } else {
  331. [self showAlert:@"会员权限不足" message:@"请升级会员"];
  332. // 更新UI显示最新状态
  333. self.memberStatus = memberStatus;
  334. [self updateUI];
  335. }
  336. });
  337. }];
  338. }
  339. #pragma mark - Helper Methods
  340. - (NSString *)getRequiredMemberType:(NSInteger)level {
  341. switch (level) {
  342. case 0: return @"free";
  343. case 1: return @"basic";
  344. case 2: return @"premium";
  345. case 3: return @"vip";
  346. default: return @"basic";
  347. }
  348. }
  349. - (NSString *)getMemberTypeDisplayName:(NSString *)memberType {
  350. if ([memberType isEqualToString:@"free"]) {
  351. return @"免费用户";
  352. } else if ([memberType isEqualToString:@"basic"]) {
  353. return @"基础会员";
  354. } else if ([memberType isEqualToString:@"premium"]) {
  355. return @"高级会员";
  356. } else if ([memberType isEqualToString:@"vip"]) {
  357. return @"VIP会员";
  358. } else {
  359. return @"未知类型";
  360. }
  361. }
  362. - (BOOL)canUpgradeToRequiredLevel:(NSString *)currentType requiredType:(NSString *)requiredType {
  363. NSInteger currentLevel = [self getMemberLevel:currentType];
  364. NSInteger requiredLevel = [self getMemberLevel:requiredType];
  365. return currentLevel < requiredLevel;
  366. }
  367. - (NSInteger)getMemberLevel:(NSString *)memberType {
  368. if ([memberType isEqualToString:@"free"]) {
  369. return 0;
  370. } else if ([memberType isEqualToString:@"basic"]) {
  371. return 1;
  372. } else if ([memberType isEqualToString:@"premium"]) {
  373. return 2;
  374. } else if ([memberType isEqualToString:@"vip"]) {
  375. return 3;
  376. } else {
  377. return 0;
  378. }
  379. }
  380. - (BOOL)supportsTrial:(NSInteger)requiredLevel {
  381. // 某些内容可能支持试看功能
  382. return requiredLevel <= 2; // 只有基础和高级内容支持试看,VIP内容不支持
  383. }
  384. - (void)showAlert:(NSString *)title message:(NSString *)message {
  385. UIAlertController *alert = [UIAlertController alertControllerWithTitle:title
  386. message:message
  387. preferredStyle:UIAlertControllerStyleAlert];
  388. [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
  389. [self presentViewController:alert animated:YES completion:nil];
  390. }
  391. - (void)dealloc {
  392. [[NSNotificationCenter defaultCenter] removeObserver:self];
  393. }
  394. @end