diff --git a/External/Pearl b/External/Pearl index a4c734e0..5c183fe3 160000 --- a/External/Pearl +++ b/External/Pearl @@ -1 +1 @@ -Subproject commit a4c734e07c7441b4a49e83ae0a5de02158095ed7 +Subproject commit 5c183fe325c7225c06910b81ac64117eb8a7aad2 diff --git a/External/iOS/Crashlytics.framework/submit b/External/iOS/Crashlytics.framework/submit index 6141e814..3febb780 100755 Binary files a/External/iOS/Crashlytics.framework/submit and b/External/iOS/Crashlytics.framework/submit differ diff --git a/MasterPassword/ObjC/iOS/MPPasswordCell.m b/MasterPassword/ObjC/iOS/MPPasswordCell.m index 30650425..78403784 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordCell.m +++ b/MasterPassword/ObjC/iOS/MPPasswordCell.m @@ -35,6 +35,7 @@ @property(nonatomic, strong) IBOutlet UIButton *editButton; @property(nonatomic, strong) IBOutlet UIScrollView *modeScrollView; @property(nonatomic, strong) IBOutlet UIButton *selectionButton; +@property(nonatomic, strong) IBOutlet UIView *indicatorView; @property(nonatomic) MPPasswordCellMode mode; @property(nonatomic, copy) NSString *transientSite; @@ -66,12 +67,19 @@ [self.selectionButton observeKeyPath:@"highlighted" withBlock:^(id from, id to, NSKeyValueChange cause, UIButton *button) { - button.layer.shadowOpacity = button.selected? 1: button.highlighted? 0.3f: 0; - }]; + button.layer.shadowOpacity = button.selected? 1: button.highlighted? 0.3f: 0; + }]; [self.selectionButton observeKeyPath:@"selected" withBlock:^(id from, id to, NSKeyValueChange cause, UIButton *button) { - button.layer.shadowOpacity = button.selected? 1: button.highlighted? 0.3f: 0; - }]; + button.layer.shadowOpacity = button.selected? 1: button.highlighted? 0.3f: 0; + }]; + + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"]; + animation.byValue = @(10); + animation.repeatCount = HUGE_VALF; + animation.autoreverses = YES; + animation.duration = 0.3f; + [self.indicatorView.layer addAnimation:animation forKey:@"bounce"]; } - (void)prepareForReuse { @@ -79,6 +87,7 @@ [super prepareForReuse]; _elementOID = nil; + self.transientSite = nil; self.loginModeButton.selected = NO; self.mode = MPPasswordCellModePassword; [self updateAnimated:NO]; @@ -182,14 +191,14 @@ [PearlSheet showSheetWithTitle:strf( @"Delete %@?", element.name ) viewStyle:UIActionSheetStyleAutomatic initSheet:nil tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) { - if (buttonIndex == [sheet cancelButtonIndex]) - return; + if (buttonIndex == [sheet cancelButtonIndex]) + return; - [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - [context deleteObject:[self elementInContext:context]]; - [context saveToStore]; - }]; - } cancelTitle:@"Cancel" destructiveTitle:@"Delete Site" otherTitles:nil]; + [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { + [context deleteObject:[self elementInContext:context]]; + [context saveToStore]; + }]; + } cancelTitle:@"Cancel" destructiveTitle:@"Delete Site" otherTitles:nil]; } - (IBAction)doChangeType:(UIButton *)sender { @@ -198,27 +207,28 @@ [PearlSheet showSheetWithTitle:@"Change Password Type" viewStyle:UIActionSheetStyleAutomatic initSheet:^(UIActionSheet *sheet) { - MPElementEntity *mainElement = [self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]; - for (NSNumber *typeNumber in [MPAlgorithmDefault allTypes]) { - MPElementType type = [typeNumber unsignedIntegerValue]; - NSString *typeName = [MPAlgorithmDefault nameOfType:type]; - if (type == mainElement.type) - [sheet addButtonWithTitle:strf( @"● %@", typeName )]; - else - [sheet addButtonWithTitle:typeName]; - } - } tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) { - if (buttonIndex == [sheet cancelButtonIndex]) - return; + MPElementEntity + *mainElement = [self elementInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]]; + for (NSNumber *typeNumber in [MPAlgorithmDefault allTypes]) { + MPElementType type = [typeNumber unsignedIntegerValue]; + NSString *typeName = [MPAlgorithmDefault nameOfType:type]; + if (type == mainElement.type) + [sheet addButtonWithTitle:strf( @"● %@", typeName )]; + else + [sheet addButtonWithTitle:typeName]; + } + } tappedButtonBlock:^(UIActionSheet *sheet, NSInteger buttonIndex) { + if (buttonIndex == [sheet cancelButtonIndex]) + return; - MPElementType type = [[MPAlgorithmDefault allTypes][buttonIndex] unsignedIntegerValue]?: MPElementTypeGeneratedLong; + MPElementType type = [[MPAlgorithmDefault allTypes][buttonIndex] unsignedIntegerValue]?: MPElementTypeGeneratedLong; - [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { - MPElementEntity *element = [self elementInContext:context]; - element = [[MPiOSAppDelegate get] changeElement:element saveInContext:context toType:type]; - [self setElement:element animated:YES]; - }]; - } cancelTitle:@"Cancel" destructiveTitle:nil otherTitles:nil]; + [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { + MPElementEntity *element = [self elementInContext:context]; + element = [[MPiOSAppDelegate get] changeElement:element saveInContext:context toType:type]; + [self setElement:element animated:YES]; + }]; + } cancelTitle:@"Cancel" destructiveTitle:nil otherTitles:nil]; } - (IBAction)doEdit:(UIButton *)sender { @@ -318,19 +328,19 @@ message:strf( @"Remember site named:\n%@", self.transientSite ) viewStyle:UIAlertViewStyleDefault initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) { - if (buttonIndex == [alert cancelButtonIndex]) { - self.selectionButton.selected = NO; - return; - } + if (buttonIndex == [alert cancelButtonIndex]) { + self.selectionButton.selected = NO; + return; + } - [[MPiOSAppDelegate get] - addElementNamed:self.transientSite completion:^(MPElementEntity *element, NSManagedObjectContext *context) { - [self copyContentOfElement:element saveInContext:context]; - PearlMainQueue( ^{ - self.selectionButton.selected = NO; - } ); - }]; - } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil]; + [[MPiOSAppDelegate get] + addElementNamed:self.transientSite completion:^(MPElementEntity *element, NSManagedObjectContext *context) { + [self copyContentOfElement:element saveInContext:context]; + PearlMainQueue( ^{ + self.selectionButton.selected = NO; + } ); + }]; + } cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil]; return; } @@ -372,13 +382,39 @@ self.passwordField.alpha = self.loginModeButton.selected? 0: 1; self.loginNameField.alpha = self.loginModeButton.selected? 1: 0; self.modeButton.alpha = self.transientSite? 0: 1; + self.loginModeButton.alpha = self.transientSite? 0: 1; self.counterLabel.alpha = self.counterButton.alpha = mainElement.type & MPElementTypeClassGenerated? 1: 0; self.modeButton.selected = self.mode == MPPasswordCellModeSettings; self.pageControl.currentPage = self.mode == MPPasswordCellModePassword? 0: 1; self.strengthLabel.alpha = self.mode == MPPasswordCellModePassword? 0: 1; self.editButton.enabled = self.loginModeButton.selected || mainElement.type & MPElementTypeClassStored; + self.modeScrollView.scrollEnabled = !self.transientSite; + self.pageControl.alpha = self.transientSite? 0: 1; [self.modeScrollView setContentOffset:CGPointMake( self.mode * self.modeScrollView.frame.size.width, 0 ) animated:animated]; + // Indicator + if (self.loginModeButton.selected) { + if ([mainElement.loginName length]) + self.indicatorView.alpha = 0; + else { + self.indicatorView.alpha = 1; + [self.indicatorView removeFromSuperview]; + [self.modeScrollView addSubview:self.indicatorView]; + [self.contentView addConstraintsWithVisualFormat:@"V:[indicator][view]" options:NSLayoutFormatAlignAllCenterX + metrics:nil views:@{ + @"indicator" : self.indicatorView, + @"view" : self.mode == MPPasswordCellModeSettings? self.editButton: self.modeButton + }]; + } + } + switch (self.mode) { + case MPPasswordCellModePassword: + if (mainElement.type & MPElementTypeClassStored) + break; + case MPPasswordCellModeSettings: + break; + } + // Site Name self.siteNameLabel.text = strl( @"%@ - %@", self.transientSite?: mainElement.name, self.transientSite? @"Tap to create": [mainElement.algorithm shortNameOfType:mainElement.type] ); @@ -389,13 +425,13 @@ self.passwordField.attributedPlaceholder = stra( mainElement.type & MPElementTypeClassStored? strl( @"No password" ): mainElement.type & MPElementTypeClassGenerated? strl( @"..." ): @"", @{ - NSForegroundColorAttributeName : [UIColor whiteColor] - } ); + NSForegroundColorAttributeName : [UIColor whiteColor] + } ); [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) { NSString *password; if (self.transientSite) password = [MPAlgorithmDefault generateContentNamed:self.transientSite ofType: - [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPElementTypeGeneratedLong + [[MPiOSAppDelegate get] activeUserInContext:context].defaultType?: MPElementTypeGeneratedLong withCounter:1 usingKey:[MPiOSAppDelegate get].key]; else password = [[self elementInContext:context] resolveContentUsingKey:[MPiOSAppDelegate get].key]; @@ -411,6 +447,21 @@ PearlMainQueue( ^{ self.passwordField.text = password; self.strengthLabel.text = timeToCrackString; + + if (!self.loginModeButton.selected) { + if ([password length]) + self.indicatorView.alpha = 0; + else { + self.indicatorView.alpha = 1; + [self.indicatorView removeFromSuperview]; + [self.modeScrollView addSubview:self.indicatorView]; + [self.contentView addConstraintsWithVisualFormat:@"V:[indicator][view]" options:NSLayoutFormatAlignAllCenterX + metrics:nil views:@{ + @"indicator" : self.indicatorView, + @"view" : self.mode == MPPasswordCellModeSettings? self.editButton: self.modeButton + }]; + } + } } ); }]; diff --git a/MasterPassword/ObjC/iOS/MPPasswordsViewController.m b/MasterPassword/ObjC/iOS/MPPasswordsViewController.m index c516ed85..9cf23a2a 100644 --- a/MasterPassword/ObjC/iOS/MPPasswordsViewController.m +++ b/MasterPassword/ObjC/iOS/MPPasswordsViewController.m @@ -22,6 +22,7 @@ #import "MPPopdownSegue.h" #import "MPAppDelegate_Key.h" #import "MPPasswordCell.h" +#import "UICollectionView+PearlReloadFromArray.h" @interface MPPasswordsViewController() @@ -36,10 +37,11 @@ NSArray *_notificationObservers; __weak UITapGestureRecognizer *_passwordsDismissRecognizer; NSFetchedResultsController *_fetchedResultsController; - BOOL _exactMatch; UIColor *_backgroundColor; UIColor *_darkenedBackgroundColor; __weak UIViewController *_popdownVC; + BOOL _showTransientItem; + NSUInteger _transientItem; } #pragma mark - Life @@ -50,6 +52,7 @@ _backgroundColor = self.passwordCollectionView.backgroundColor; _darkenedBackgroundColor = [_backgroundColor colorWithAlphaComponent:0.6f]; + _transientItem = NSNotFound; self.view.backgroundColor = [UIColor clearColor]; [self.passwordCollectionView automaticallyAdjustInsetsForKeyboard]; @@ -115,9 +118,12 @@ referenceSizeForHeaderInSection:(NSInteger)section { - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return ![MPiOSAppDelegate get].activeUserOID? 0: - ((id)self.fetchedResultsController.sections[section]).numberOfObjects + - (!_exactMatch && [[self query] length]? 1: 0); + if (![MPiOSAppDelegate get].activeUserOID) + return 0; + + NSUInteger objects = ((id)self.fetchedResultsController.sections[section]).numberOfObjects; + _transientItem = _showTransientItem? objects: NSNotFound; + return objects + (_showTransientItem? 1: 0); } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { @@ -152,20 +158,23 @@ referenceSizeForHeaderInSection:(NSInteger)section { forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { if (controller == _fetchedResultsController) { - switch (type) { - case NSFetchedResultsChangeInsert: - [self.passwordCollectionView insertItemsAtIndexPaths:@[ newIndexPath ]]; - break; - case NSFetchedResultsChangeDelete: - [self.passwordCollectionView deleteItemsAtIndexPaths:@[ indexPath ]]; - break; - case NSFetchedResultsChangeMove: - [self.passwordCollectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath]; - break; - case NSFetchedResultsChangeUpdate: - [self.passwordCollectionView reloadItemsAtIndexPaths:@[ indexPath ]]; - break; - } + [self.passwordCollectionView performBatchUpdates:^{ + [self fetchedItemsDidUpdate]; + switch (type) { + case NSFetchedResultsChangeInsert: + [self.passwordCollectionView insertItemsAtIndexPaths:@[ newIndexPath ]]; + break; + case NSFetchedResultsChangeDelete: + [self.passwordCollectionView deleteItemsAtIndexPaths:@[ indexPath ]]; + break; + case NSFetchedResultsChangeMove: + [self.passwordCollectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath]; + break; + case NSFetchedResultsChangeUpdate: + [self.passwordCollectionView reloadItemsAtIndexPaths:@[ indexPath ]]; + break; + } + } completion:nil]; } } @@ -229,6 +238,33 @@ referenceSizeForHeaderInSection:(NSInteger)section { #pragma mark - Private +- (void)fetchedItemsDidUpdate { + + NSString *query = self.query; + _showTransientItem = [query length] > 0; + NSUInteger objects = ((id)self.fetchedResultsController.sections[0]).numberOfObjects; + if (_showTransientItem && objects == 1 && + [[[self.fetchedResultsController.fetchedObjects firstObject] name] isEqualToString:query]) + _showTransientItem = NO; + if ([self.passwordCollectionView numberOfSections] > 0) { + if (!_showTransientItem && _transientItem != NSNotFound) { + dbg( @"delete transient item: %d", [self.passwordCollectionView numberOfItemsInSection:0] - 1 ); + [self.passwordCollectionView deleteItemsAtIndexPaths: + @[ [NSIndexPath indexPathForItem:_transientItem inSection:0] ]]; + } + else if (_showTransientItem && _transientItem == NSNotFound) { + dbg( @"insert transient item: %d", objects ); + [self.passwordCollectionView insertItemsAtIndexPaths: + @[ [NSIndexPath indexPathForItem:objects inSection:0] ]]; + } + else if (_transientItem != NSNotFound) { + dbg( @"reload transient item: %d", objects ); + [self.passwordCollectionView reloadItemsAtIndexPaths: + @[ [NSIndexPath indexPathForItem:_transientItem inSection:0] ]]; + } + } +} + - (void)registerObservers { if ([_notificationObservers count]) @@ -239,34 +275,34 @@ referenceSizeForHeaderInSection:(NSInteger)section { [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - Strongify( self ); + Strongify( self ); - self.passwordSelectionContainer.alpha = 0; - }], + self.passwordSelectionContainer.alpha = 0; + }], [[NSNotificationCenter defaultCenter] addObserverForName:MPSignedOutNotification object:nil queue:nil usingBlock:^(NSNotification *note) { - Strongify( self ); + Strongify( self ); - _fetchedResultsController = nil; - self.passwordsSearchBar.text = nil; - [self updatePasswords]; - }], + _fetchedResultsController = nil; + self.passwordsSearchBar.text = nil; + [self updatePasswords]; + }], [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - Strongify( self ); + Strongify( self ); - [self updatePasswords]; - [UIView animateWithDuration:1 animations:^{ - self.passwordSelectionContainer.alpha = 1; - }]; - }], + [self updatePasswords]; + [UIView animateWithDuration:1 animations:^{ + self.passwordSelectionContainer.alpha = 1; + }]; + }], [[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { - [self updateConfigKey:note.object]; - }], + [self updateConfigKey:note.object]; + }], ]; } @@ -329,6 +365,8 @@ referenceSizeForHeaderInSection:(NSInteger)section { } [self.fetchedResultsController.managedObjectContext performBlock:^{ + NSArray *oldSections = [self.fetchedResultsController sections]; + NSError *error = nil; self.fetchedResultsController.fetchRequest.predicate = [query length]? @@ -337,37 +375,32 @@ referenceSizeForHeaderInSection:(NSInteger)section { if (![self.fetchedResultsController performFetch:&error]) err( @"Couldn't fetch elements: %@", error ); - _exactMatch = NO; - for (MPElementEntity *entity in self.fetchedResultsController.fetchedObjects) - if ([entity.name isEqualToString:query]) { - _exactMatch = YES; - break; - } + [self.passwordCollectionView performBatchUpdates:^{ + [self fetchedItemsDidUpdate]; - PearlMainQueue( ^{ - [self.passwordCollectionView performBatchUpdates:^{ - NSInteger fromSections = self.passwordCollectionView.numberOfSections; - NSInteger toSections = [self numberOfSectionsInCollectionView:self.passwordCollectionView]; - for (int section = 0; section < MAX( toSections, fromSections ); section++) { - if (section >= fromSections) { - dbg( @"insertSections:%d", section ); - [self.passwordCollectionView insertSections:[NSIndexSet indexSetWithIndex:section]]; - } - else if (section >= toSections) { - dbg( @"deleteSections:%d", section ); - [self.passwordCollectionView deleteSections:[NSIndexSet indexSetWithIndex:section]]; - } - else { - dbg( @"reloadSections:%d", section ); - [self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:section]]; - } + NSInteger fromSections = self.passwordCollectionView.numberOfSections; + NSInteger toSections = [self numberOfSectionsInCollectionView:self.passwordCollectionView]; + for (NSInteger section = 0; section < MAX( toSections, fromSections ); ++section) { + if (section >= fromSections) { + dbg( @"insertSections:%d", section ); + [self.passwordCollectionView insertSections:[NSIndexSet indexSetWithIndex:section]]; } - } completion:^(BOOL finished) { - if (finished) - [self.passwordCollectionView setContentOffset:CGPointMake( 0, -self.passwordCollectionView.contentInset.top ) - animated:YES]; - }]; - } ); + else if (section >= toSections) { + dbg( @"deleteSections:%d", section ); + [self.passwordCollectionView deleteSections:[NSIndexSet indexSetWithIndex:section]]; + } + else { + dbg( @"reloadItemsInSection:%d", section ); + [self.passwordCollectionView reloadItemsFromArray:[oldSections[section] objects] + toArray:[[self.fetchedResultsController sections][section] objects] + inSection:section]; + } + } + } completion:^(BOOL finished) { + if (finished) + [self.passwordCollectionView setContentOffset:CGPointMake( 0, -self.passwordCollectionView.contentInset.top ) + animated:YES]; + }]; }]; } diff --git a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj index 996ccf7b..30101388 100644 --- a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj +++ b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ 93D3992FA1546E01F498F665 /* PearlNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D398567FD02DB2647B8CF3 /* PearlNavigationController.h */; }; 93D399433EA75E50656040CB /* Twitter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93D394077F8FAB8167647187 /* Twitter.framework */; }; 93D399BBC0A7EC746CB1B19B /* MPLogsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D391943675426839501BB8 /* MPLogsViewController.h */; }; + 93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */; }; 93D39A5FF670957C0AF8298D /* MPPasswordCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */; }; 93D39A8EA1C49CE43B63F47B /* PearlUICollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39D8A953779B35403AF6E /* PearlUICollectionView.m */; }; 93D39B76DD5AB108BA8928E8 /* UIScrollView+PearlAdjustInsets.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39DE2CB351D4E3789462B /* UIScrollView+PearlAdjustInsets.h */; }; @@ -41,6 +42,7 @@ 93D39B8F90F58A5D158DDBA3 /* MPPasswordsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */; }; 93D39BA1EA3CAAC8A220B4A6 /* MPAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */; }; 93D39C34FE35830EF5BE1D2A /* NSArray+Indexing.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D396D04E57792A54D437AC /* NSArray+Indexing.h */; }; + 93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadFromArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */; }; 93D39D596A2E376D6F6F5DA1 /* MPCombinedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D393310223DDB35218467A /* MPCombinedViewController.m */; }; 93D39D8F78978196D6ABDEDE /* MPNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39CC01630D0421205C4C4 /* MPNavigationController.m */; }; 93D39E281E3658B30550CB55 /* NSDictionary+Indexing.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39AA1EE2E1E7B81372240 /* NSDictionary+Indexing.m */; }; @@ -381,12 +383,14 @@ /* Begin PBXFileReference section */ 93D390519405B76CC6A57C4F /* MPCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCell.h; sourceTree = ""; }; 93D39067C0AFDC581794E2B8 /* NSArray+Indexing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Indexing.m"; sourceTree = ""; }; + 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionView+PearlReloadFromArray.m"; sourceTree = ""; }; 93D390FADEB325D8D54A957D /* PearlOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlOverlay.m; sourceTree = ""; }; 93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+PearlAdjustInsets.m"; sourceTree = ""; }; 93D3914D7597F9A28DB9D85E /* MPPasswordsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsViewController.h; sourceTree = ""; }; 93D39156E806BB78E04F78B9 /* PearlSizedTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlSizedTextView.m; sourceTree = ""; }; 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppSettingsViewController.m; sourceTree = ""; }; 93D391943675426839501BB8 /* MPLogsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLogsViewController.h; sourceTree = ""; }; + 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionView+PearlReloadFromArray.h"; sourceTree = ""; }; 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPasswordsViewController.m; sourceTree = ""; }; 93D392876BE5C011DE73B43F /* MPPopdownSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPopdownSegue.h; sourceTree = ""; }; 93D393310223DDB35218467A /* MPCombinedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCombinedViewController.m; sourceTree = ""; }; @@ -2628,6 +2632,8 @@ 93D39A4759186F6D2D34AA6B /* PearlSizedTextView.h */, 93D3977321EB249981821AB0 /* UITextView+PearlAttributes.m */, 93D39AA10CD00D05937671B1 /* UITextView+PearlAttributes.h */, + 93D3908DF8EABBD952065DC0 /* UICollectionView+PearlReloadFromArray.m */, + 93D39246FC21C6E63E35D615 /* UICollectionView+PearlReloadFromArray.h */, ); path = "Pearl-UIKit"; sourceTree = ""; @@ -2741,6 +2747,7 @@ 93D39536EB550E811CCD04BC /* UIResponder+PearlFirstResponder.h in Headers */, 93D393DB5325820241BA90A7 /* PearlSizedTextView.h in Headers */, 93D392A8777DC30C11361647 /* UITextView+PearlAttributes.h in Headers */, + 93D39A53D76CA70786423458 /* UICollectionView+PearlReloadFromArray.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3308,6 +3315,7 @@ DAA141201922FF020032B392 /* PearlTween.m in Sources */, 93D391ECBD9BD2C64115B5DD /* PearlSizedTextView.m in Sources */, 93D39E34FD28D24FE3442C48 /* UITextView+PearlAttributes.m in Sources */, + 93D39D47FC623E91FC39D20C /* UICollectionView+PearlReloadFromArray.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (Development).xcscheme b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (Development).xcscheme index d563d146..6408aad5 100644 --- a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (Development).xcscheme +++ b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/xcshareddata/xcschemes/MasterPassword iOS (Development).xcscheme @@ -63,7 +63,7 @@ isEnabled = "YES"> diff --git a/MasterPassword/ObjC/iOS/Storyboard.storyboard b/MasterPassword/ObjC/iOS/Storyboard.storyboard index 2007bfe4..5c945e46 100644 --- a/MasterPassword/ObjC/iOS/Storyboard.storyboard +++ b/MasterPassword/ObjC/iOS/Storyboard.storyboard @@ -1262,6 +1262,13 @@ + @@ -1273,6 +1280,7 @@ + @@ -1280,6 +1288,7 @@ + @@ -1337,6 +1346,7 @@ + @@ -2439,6 +2449,6 @@ See - +