From 19d8f6cf01ac15c41883938fac94c4d842ea8a8d Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 6 Apr 2017 11:44:03 -0400 Subject: [PATCH 1/2] Improvements around contact cells. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Improve handling of accessory views/types. * Use contact cell in “new/edit group” view. * Don’t hide blocked contacts and group members in “new/edit group” view. * Let users “unblock-to-add” blocked contacts in “new/edit group” view. // FREEBIE --- .../AddToBlockListViewController.m | 8 +- Signal/src/ViewControllers/BlockListUIUtils.h | 10 + Signal/src/ViewControllers/BlockListUIUtils.m | 92 +++++++-- .../MessageComposeTableViewController.m | 8 +- .../ViewControllers/NewGroupViewController.m | 175 ++++++++++++++---- Signal/src/views/ContactTableViewCell.h | 4 +- Signal/src/views/ContactTableViewCell.m | 17 +- .../translations/en.lproj/Localizable.strings | 11 +- 8 files changed, 263 insertions(+), 62 deletions(-) diff --git a/Signal/src/ViewControllers/AddToBlockListViewController.m b/Signal/src/ViewControllers/AddToBlockListViewController.m index 6359fdb2b..8aa290299 100644 --- a/Signal/src/ViewControllers/AddToBlockListViewController.m +++ b/Signal/src/ViewControllers/AddToBlockListViewController.m @@ -501,7 +501,13 @@ NSString *const kContactsTable_CellReuseIdentifier = @"kContactsTable_CellReuseI if (!cell) { cell = [ContactTableViewCell new]; } - cell.isBlocked = [self isContactBlocked:contact]; + BOOL isBlocked = [self isContactBlocked:contact]; + if (isBlocked) { + cell.accessoryMessage + = NSLocalizedString(@"CONTACT_CELL_IS_BLOCKED", @"An indicator that a contact has been blocked."); + } else { + OWSAssert(cell.accessoryMessage == nil); + } [cell configureWithContact:contact contactsManager:self.contactsManager]; return cell; } diff --git a/Signal/src/ViewControllers/BlockListUIUtils.h b/Signal/src/ViewControllers/BlockListUIUtils.h index f1cf0671c..a7b6654bd 100644 --- a/Signal/src/ViewControllers/BlockListUIUtils.h +++ b/Signal/src/ViewControllers/BlockListUIUtils.h @@ -16,6 +16,8 @@ typedef void (^BlockActionCompletionBlock)(BOOL isBlocked); - (instancetype)init NS_UNAVAILABLE; +#pragma mark - Block + + (void)showBlockContactActionSheet:(Contact *)contact fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager @@ -28,6 +30,14 @@ typedef void (^BlockActionCompletionBlock)(BOOL isBlocked); contactsManager:(OWSContactsManager *)contactsManager completionBlock:(nullable BlockActionCompletionBlock)completionBlock; +#pragma mark - Unblock + ++ (void)showUnblockContactActionSheet:(Contact *)contact + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + contactsManager:(OWSContactsManager *)contactsManager + completionBlock:(nullable BlockActionCompletionBlock)completionBlock; + + (void)showUnblockPhoneNumberActionSheet:(NSString *)phoneNumber fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager diff --git a/Signal/src/ViewControllers/BlockListUIUtils.m b/Signal/src/ViewControllers/BlockListUIUtils.m index ba329101e..ffa72572a 100644 --- a/Signal/src/ViewControllers/BlockListUIUtils.m +++ b/Signal/src/ViewControllers/BlockListUIUtils.m @@ -14,6 +14,8 @@ typedef void (^BlockAlertCompletionBlock)(); @implementation BlockListUIUtils +#pragma mark - Block + + (void)showBlockContactActionSheet:(Contact *)contact fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager @@ -133,18 +135,63 @@ typedef void (^BlockAlertCompletionBlock)(); completionBlock:completionBlock]; } +#pragma mark - Unblock + ++ (void)showUnblockContactActionSheet:(Contact *)contact + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + contactsManager:(OWSContactsManager *)contactsManager + completionBlock:(nullable BlockActionCompletionBlock)completionBlock +{ + NSMutableArray *phoneNumbers = [NSMutableArray new]; + for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) { + if (phoneNumber.toE164.length > 0) { + [phoneNumbers addObject:phoneNumber.toE164]; + } + } + if (phoneNumbers.count < 1) { + DDLogError(@"%@ Contact has no phone numbers", self.tag); + OWSAssert(0); + [self showUnblockFailedAlert:fromViewController + completionBlock:^{ + if (completionBlock) { + completionBlock(NO); + } + }]; + return; + } + NSString *displayName = [contactsManager displayNameForContact:contact]; + [self showUnblockPhoneNumbersActionSheet:phoneNumbers + displayName:displayName + fromViewController:fromViewController + blockingManager:blockingManager + completionBlock:completionBlock]; +} + + (void)showUnblockPhoneNumberActionSheet:(NSString *)phoneNumber fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager contactsManager:(OWSContactsManager *)contactsManager completionBlock:(nullable BlockActionCompletionBlock)completionBlock { - OWSAssert(phoneNumber.length > 0); + NSString *displayName = [contactsManager displayNameForPhoneIdentifier:phoneNumber]; + [self showUnblockPhoneNumbersActionSheet:@[ phoneNumber ] + displayName:displayName + fromViewController:fromViewController + blockingManager:blockingManager + completionBlock:completionBlock]; +} + ++ (void)showUnblockPhoneNumbersActionSheet:(NSArray *)phoneNumbers + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(nullable BlockActionCompletionBlock)completionBlock +{ + OWSAssert(phoneNumbers.count > 0); + OWSAssert(displayName.length > 0); OWSAssert(fromViewController); OWSAssert(blockingManager); - OWSAssert(contactsManager); - - NSString *displayName = [contactsManager displayNameForPhoneIdentifier:phoneNumber]; NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_TITLE_FORMAT", @"A format for the 'unblock user' action sheet title. Embeds " @@ -158,15 +205,15 @@ typedef void (^BlockAlertCompletionBlock)(); actionWithTitle:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_BUTTON", @"Button label for the 'unblock' button") style:UIAlertActionStyleDestructive handler:^(UIAlertAction *_Nonnull action) { - [BlockListUIUtils unblockPhoneNumber:phoneNumber - displayName:displayName - fromViewController:fromViewController - blockingManager:blockingManager - completionBlock:^{ - if (completionBlock) { - completionBlock(NO); - } - }]; + [BlockListUIUtils unblockPhoneNumbers:phoneNumbers + displayName:displayName + fromViewController:fromViewController + blockingManager:blockingManager + completionBlock:^{ + if (completionBlock) { + completionBlock(NO); + } + }]; }]; [actionSheetController addAction:unblockAction]; @@ -182,18 +229,21 @@ typedef void (^BlockAlertCompletionBlock)(); [fromViewController presentViewController:actionSheetController animated:YES completion:nil]; } -+ (void)unblockPhoneNumber:(NSString *)phoneNumber - displayName:(NSString *)displayName - fromViewController:(UIViewController *)fromViewController - blockingManager:(OWSBlockingManager *)blockingManager - completionBlock:(BlockAlertCompletionBlock)completionBlock ++ (void)unblockPhoneNumbers:(NSArray *)phoneNumbers + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockAlertCompletionBlock)completionBlock { - OWSAssert(phoneNumber.length > 0); + OWSAssert(phoneNumbers.count > 0); OWSAssert(displayName.length > 0); OWSAssert(fromViewController); OWSAssert(blockingManager); - [blockingManager removeBlockedPhoneNumber:phoneNumber]; + for (NSString *phoneNumber in phoneNumbers) { + OWSAssert(phoneNumber.length > 0); + [blockingManager removeBlockedPhoneNumber:phoneNumber]; + } [self showOkAlertWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_TITLE", @"The title of the 'user unblocked' alert.") @@ -232,6 +282,8 @@ typedef void (^BlockAlertCompletionBlock)(); completionBlock:completionBlock]; } +#pragma mark - UI + + (void)showOkAlertWithTitle:(NSString *)title message:(NSString *)message fromViewController:(UIViewController *)fromViewController diff --git a/Signal/src/ViewControllers/MessageComposeTableViewController.m b/Signal/src/ViewControllers/MessageComposeTableViewController.m index dc377d08f..a8b77e435 100644 --- a/Signal/src/ViewControllers/MessageComposeTableViewController.m +++ b/Signal/src/ViewControllers/MessageComposeTableViewController.m @@ -622,7 +622,13 @@ NSString *const MessageComposeTableViewControllerCellContact = @"ContactTableVie dequeueReusableCellWithIdentifier:MessageComposeTableViewControllerCellContact]; Contact *contact = [self contactForIndexPath:indexPath]; - cell.isBlocked = [self isContactBlocked:contact]; + BOOL isBlocked = [self isContactBlocked:contact]; + if (isBlocked) { + cell.accessoryMessage + = NSLocalizedString(@"CONTACT_CELL_IS_BLOCKED", @"An indicator that a contact has been blocked."); + } else { + OWSAssert(cell.accessoryMessage == nil); + } [cell configureWithContact:contact contactsManager:self.contactsManager]; return cell; diff --git a/Signal/src/ViewControllers/NewGroupViewController.m b/Signal/src/ViewControllers/NewGroupViewController.m index 6c8b84415..241db2e6a 100644 --- a/Signal/src/ViewControllers/NewGroupViewController.m +++ b/Signal/src/ViewControllers/NewGroupViewController.m @@ -3,6 +3,8 @@ // #import "NewGroupViewController.h" +#import "BlockListUIUtils.h" +#import "ContactTableViewCell.h" #import "Environment.h" #import "FunctionalUtil.h" #import "OWSContactsManager.h" @@ -107,18 +109,50 @@ static NSString *const kUnwindToMessagesViewSegue = @"UnwindToMessagesViewSegue" - (void)updateContacts { AssertIsOnMainThread(); + // Snapshot selection state. + NSMutableSet *selectedContacts = [NSMutableSet set]; + for (NSIndexPath *indexPath in [self.tableView indexPathsForSelectedRows]) { + Contact *contact = contacts[(NSUInteger)indexPath.row]; + [selectedContacts addObject:contact]; + } + contacts = [self filteredContacts]; [self.tableView reloadData]; + + // Restore selection state. + for (Contact *contact in selectedContacts) { + if ([contacts containsObject:contact]) { + NSInteger row = (NSInteger)[contacts indexOfObject:contact]; + [self.tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0] + animated:NO + scrollPosition:UITableViewScrollPositionNone]; + } + } } -- (BOOL)isContactBlockedOrHidden:(Contact *)contact +- (BOOL)isContactHidden:(Contact *)contact { if (contact.parsedPhoneNumbers.count < 1) { // Hide contacts without any valid phone numbers. return YES; } + if ([self isCurrentUserContact:contact]) { + // We never want to add ourselves to a group. + return YES; + } + + return NO; +} + +- (BOOL)isContactBlocked:(Contact *)contact +{ + if (contact.parsedPhoneNumbers.count < 1) { + // Hide contacts without any valid phone numbers. + return NO; + } + for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) { if ([_blockedPhoneNumbers containsObject:phoneNumber.toE164]) { return YES; @@ -139,6 +173,17 @@ static NSString *const kUnwindToMessagesViewSegue = @"UnwindToMessagesViewSegue" return NO; } +- (NSArray *_Nonnull)filteredContacts +{ + NSMutableArray *result = [NSMutableArray new]; + for (Contact *contact in self.contactsManager.signalContacts) { + if (![self isContactHidden:contact]) { + [result addObject:contact]; + } + } + return [result copy]; +} + - (BOOL)isContactInGroup:(Contact *)contact { for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) { @@ -155,18 +200,6 @@ static NSString *const kUnwindToMessagesViewSegue = @"UnwindToMessagesViewSegue" return NO; } -- (NSArray *_Nonnull)filteredContacts -{ - NSMutableArray *result = [NSMutableArray new]; - for (Contact *contact in self.contactsManager.signalContacts) { - if (![self isContactBlockedOrHidden:contact] && ![self isCurrentUserContact:contact] - && ![self isContactInGroup:contact]) { - [result addObject:contact]; - } - } - return [result copy]; -} - - (void)configWithThread:(TSGroupThread *)gThread { _thread = gThread; } @@ -421,39 +454,111 @@ static NSString *const kUnwindToMessagesViewSegue = @"UnwindToMessagesViewSegue" } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SearchCell"]; - - if (cell == nil) { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"GroupSearchCell"]; + ContactTableViewCell *cell + = (ContactTableViewCell *)[tableView dequeueReusableCellWithIdentifier:[ContactTableViewCell reuseIdentifier]]; + if (!cell) { + cell = [ContactTableViewCell new]; } - NSUInteger row = (NSUInteger)indexPath.row; - Contact *contact = contacts[row]; - - cell.textLabel.attributedText = [self.contactsManager formattedFullNameForContact:contact font:cell.textLabel.font]; - - tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero]; - - if ([[tableView indexPathsForSelectedRows] containsObject:indexPath]) { - [self adjustSelected:cell]; - } + [self updateContentsOfCell:cell indexPath:indexPath]; return cell; } -#pragma mark - Table View delegate -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; - [self adjustSelected:cell]; +- (void)updateContentsOfCell:(ContactTableViewCell *)cell indexPath:(NSIndexPath *)indexPath +{ + OWSAssert(cell); + OWSAssert(indexPath); + + Contact *contact = contacts[(NSUInteger)indexPath.row]; + BOOL isBlocked = [self isContactBlocked:contact]; + BOOL isInGroup = [self isContactInGroup:contact]; + BOOL isSelected = [[self.tableView indexPathsForSelectedRows] containsObject:indexPath]; + // More than one of these conditions might be true. + // In order of priority... + cell.accessoryMessage = nil; + cell.accessoryView = nil; + cell.accessoryType = UITableViewCellAccessoryNone; + if (isInGroup) { + OWSAssert(!isSelected); + // ...if the user is already in the group, indicate that. + cell.accessoryMessage = NSLocalizedString( + @"CONTACT_CELL_IS_IN_GROUP", @"An indicator that a contact is a member of the current group."); + } else if (isSelected) { + // ...if the user is being added to the group, indicate that. + cell.accessoryType = UITableViewCellAccessoryCheckmark; + } else if (isBlocked) { + // ...if the user is blocked, indicate that. + cell.accessoryMessage + = NSLocalizedString(@"CONTACT_CELL_IS_BLOCKED", @"An indicator that a contact has been blocked."); + } + [cell configureWithContact:contact contactsManager:self.contactsManager]; } -- (void)adjustSelected:(UITableViewCell *)cell { - cell.accessoryType = UITableViewCellAccessoryCheckmark; +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + return [ContactTableViewCell rowHeight]; +} + +#pragma mark - Table View delegate + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + Contact *contact = contacts[(NSUInteger)indexPath.row]; + BOOL isBlocked = [self isContactBlocked:contact]; + BOOL isInGroup = [self isContactInGroup:contact]; + if (isInGroup) { + // Deselect. + [tableView deselectRowAtIndexPath:indexPath animated:YES]; + + NSString *displayName = [_contactsManager displayNameForContact:contact]; + UIAlertController *controller = [UIAlertController + alertControllerWithTitle: + NSLocalizedString(@"EDIT_GROUP_VIEW_ALREADY_IN_GROUP_ALERT_TITLE", + @"A title of the alert if user tries to add a user to a group who is already in the group.") + message:[NSString + stringWithFormat: + NSLocalizedString(@"EDIT_GROUP_VIEW_ALREADY_IN_GROUP_ALERT_MESSAGE_FORMAT", + @"A format for the message of the alert if user tries to " + @"add a user to a group who is already in the group. Embeds {{the " + @"blocked user's name or phone number}}."), + displayName] + preferredStyle:UIAlertControllerStyleAlert]; + [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) + style:UIAlertActionStyleDefault + handler:nil]]; + [self presentViewController:controller animated:YES completion:nil]; + return; + } else if (isBlocked) { + // Deselect. + [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; + + __weak NewGroupViewController *weakSelf = self; + [BlockListUIUtils showUnblockContactActionSheet:contact + fromViewController:self + blockingManager:_blockingManager + contactsManager:_contactsManager + completionBlock:^(BOOL isStillBlocked) { + if (!isStillBlocked) { + // Re-select. + [weakSelf.tableView selectRowAtIndexPath:indexPath + animated:YES + scrollPosition:UITableViewScrollPositionNone]; + + ContactTableViewCell *cell = (ContactTableViewCell *)[weakSelf.tableView + cellForRowAtIndexPath:indexPath]; + [weakSelf updateContentsOfCell:cell indexPath:indexPath]; + } + }]; + return; + } + + ContactTableViewCell *cell = (ContactTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath]; + [self updateContentsOfCell:cell indexPath:indexPath]; } - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath { - UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; - cell.accessoryType = UITableViewCellAccessoryNone; + ContactTableViewCell *cell = (ContactTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath]; + [self updateContentsOfCell:cell indexPath:indexPath]; } #pragma mark - Text Field Delegate diff --git a/Signal/src/views/ContactTableViewCell.h b/Signal/src/views/ContactTableViewCell.h index bc635c37d..d9687eb72 100644 --- a/Signal/src/views/ContactTableViewCell.h +++ b/Signal/src/views/ContactTableViewCell.h @@ -17,7 +17,9 @@ NS_ASSUME_NONNULL_BEGIN @interface ContactTableViewCell : UITableViewCell -@property (nonatomic) BOOL isBlocked; +@property (nonatomic, nullable) NSString *accessoryMessage; + ++ (nullable NSString *)reuseIdentifier; + (CGFloat)rowHeight; diff --git a/Signal/src/views/ContactTableViewCell.m b/Signal/src/views/ContactTableViewCell.m index 673a1afbe..31ef476ae 100644 --- a/Signal/src/views/ContactTableViewCell.m +++ b/Signal/src/views/ContactTableViewCell.m @@ -29,6 +29,11 @@ NS_ASSUME_NONNULL_BEGIN return self; } ++ (nullable NSString *)reuseIdentifier +{ + return NSStringFromClass(self.class); +} + - (nullable NSString *)reuseIdentifier { return NSStringFromClass(self.class); @@ -70,11 +75,10 @@ NS_ASSUME_NONNULL_BEGIN { NSMutableAttributedString *attributedText = [[contactsManager formattedFullNameForContact:contact font:self.nameLabel.font] mutableCopy]; - if (self.isBlocked) { + if (self.accessoryMessage) { UILabel *blockedLabel = [[UILabel alloc] init]; blockedLabel.textAlignment = NSTextAlignmentRight; - blockedLabel.text - = NSLocalizedString(@"CONTACT_BLOCKED_INDICATOR", @"An indicator that a contact has been blocked."); + blockedLabel.text = self.accessoryMessage; blockedLabel.font = [UIFont ows_mediumFontWithSize:13.f]; blockedLabel.textColor = [UIColor colorWithWhite:0.5f alpha:1.f]; [blockedLabel sizeToFit]; @@ -97,6 +101,13 @@ NS_ASSUME_NONNULL_BEGIN [UIUtil applyRoundedBorderToImageView:self.avatarView]; } +- (void)prepareForReuse +{ + self.accessoryMessage = nil; + self.accessoryView = nil; + self.accessoryType = UITableViewCellAccessoryNone; +} + @end NS_ASSUME_NONNULL_END diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 7dc4c8395..1e349da79 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -194,7 +194,10 @@ "CONFIRMATION_TITLE" = "Confirm"; /* An indicator that a contact has been blocked. */ -"CONTACT_BLOCKED_INDICATOR" = "(Blocked)"; +"CONTACT_CELL_IS_BLOCKED" = "Blocked"; + +/* An indicator that a contact is a member of the current group. */ +"CONTACT_CELL_IS_IN_GROUP" = "Group Member"; /* No comment provided by engineer. */ "CONTACT_DETAIL_COMM_TYPE_INSECURE" = "Unregistered Number"; @@ -256,6 +259,12 @@ /* table cell label in conversation settings */ "EDIT_GROUP_ACTION" = "Edit Group"; +/* A format for the message of the alert if user tries to add a user to a group who is already in the group. Embeds {{the blocked user's name or phone number}}. */ +"EDIT_GROUP_VIEW_ALREADY_IN_GROUP_ALERT_MESSAGE_FORMAT" = "%@ is already a member of this group."; + +/* A title of the alert if user tries to add a user to a group who is already in the group. */ +"EDIT_GROUP_VIEW_ALREADY_IN_GROUP_ALERT_TITLE" = "Already a Group Member"; + /* Short name for edit menu item to copy contents of media message. */ "EDIT_ITEM_COPY_ACTION" = "Copy"; From cc16b9c893b9e20a6bda7df813131dbecd216f62 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 6 Apr 2017 20:58:42 -0400 Subject: [PATCH 2/2] CR nit: add assert // FREEBIE --- Signal/src/ViewControllers/NewGroupViewController.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Signal/src/ViewControllers/NewGroupViewController.m b/Signal/src/ViewControllers/NewGroupViewController.m index 241db2e6a..cfd0e41fd 100644 --- a/Signal/src/ViewControllers/NewGroupViewController.m +++ b/Signal/src/ViewControllers/NewGroupViewController.m @@ -471,6 +471,8 @@ static NSString *const kUnwindToMessagesViewSegue = @"UnwindToMessagesViewSegue" OWSAssert(indexPath); Contact *contact = contacts[(NSUInteger)indexPath.row]; + OWSAssert(contact != nil); + BOOL isBlocked = [self isContactBlocked:contact]; BOOL isInGroup = [self isContactInGroup:contact]; BOOL isSelected = [[self.tableView indexPathsForSelectedRows] containsObject:indexPath];