Bugfixes with regards to swiping password types.
This commit is contained in:
parent
943c378206
commit
fa57b8817b
2
External/Pearl
vendored
2
External/Pearl
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 7ed74fd245f47339903ab2faeb3f36754155452c
|
Subproject commit d37640cc0133e56b1d58baf7ed44ce69dcb20f2f
|
@ -21,7 +21,7 @@
|
|||||||
+ (instancetype)get;
|
+ (instancetype)get;
|
||||||
|
|
||||||
- (MPUserEntity *)activeUserForMainThread;
|
- (MPUserEntity *)activeUserForMainThread;
|
||||||
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)moc;
|
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)context;
|
||||||
- (void)setActiveUser:(MPUserEntity *)activeUser;
|
- (void)setActiveUser:(MPUserEntity *)activeUser;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -28,18 +28,15 @@
|
|||||||
return [self activeUserInContext:[MPAppDelegate_Shared managedObjectContextForMainThreadIfReady]];
|
return [self activeUserInContext:[MPAppDelegate_Shared managedObjectContextForMainThreadIfReady]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)moc {
|
- (MPUserEntity *)activeUserInContext:(NSManagedObjectContext *)context {
|
||||||
|
|
||||||
NSManagedObjectID *activeUserOID = self.activeUserOID;
|
NSManagedObjectID *activeUserOID = self.activeUserOID;
|
||||||
if (!activeUserOID || !moc)
|
if (!activeUserOID || !context)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
NSError *error;
|
MPUserEntity *activeUser = [MPUserEntity existingObjectWithID:activeUserOID inContext:context];
|
||||||
MPUserEntity *activeUser = (MPUserEntity *)[moc existingObjectWithID:activeUserOID error:&error];
|
if (!activeUser)
|
||||||
if (!activeUser) {
|
|
||||||
[self signOutAnimated:YES];
|
[self signOutAnimated:YES];
|
||||||
err(@"Failed to retrieve active user: %@", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return activeUser;
|
return activeUser;
|
||||||
}
|
}
|
||||||
|
@ -260,12 +260,9 @@ forRowAtIndexPath:(NSIndexPath *)indexPath {
|
|||||||
if (editingStyle == UITableViewCellEditingStyleDelete) {
|
if (editingStyle == UITableViewCellEditingStyleDelete) {
|
||||||
NSManagedObjectID *elementOID = [self elementForTableIndexPath:indexPath].objectID;
|
NSManagedObjectID *elementOID = [self elementForTableIndexPath:indexPath].objectID;
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlockAndWait:^(NSManagedObjectContext *context) {
|
||||||
NSError *error = nil;
|
MPElementEntity *element = [MPElementEntity existingObjectWithID:elementOID inContext:context];
|
||||||
MPElementEntity *element = (MPElementEntity *)[context existingObjectWithID:elementOID error:&error];
|
if (!element)
|
||||||
if (!element) {
|
|
||||||
err(@"Failed to retrieve element to delete: %@", error);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
inf(@"Deleting element: %@", element.name);
|
inf(@"Deleting element: %@", element.name);
|
||||||
[context deleteObject:element];
|
[context deleteObject:element];
|
||||||
|
@ -18,10 +18,12 @@
|
|||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "MPPasswordCell.h"
|
#import "MPPasswordCell.h"
|
||||||
|
#import "MPPasswordsViewController.h"
|
||||||
|
|
||||||
@interface MPPasswordElementCell : MPPasswordCell
|
@interface MPPasswordElementCell : MPPasswordCell
|
||||||
|
|
||||||
@property(nonatomic, copy) NSString *transientSite;
|
@property(nonatomic, copy) NSString *transientSite;
|
||||||
|
@property(nonatomic, weak) MPPasswordsViewController *passwordsViewController;
|
||||||
|
|
||||||
- (MPElementEntity *)mainElement;
|
- (MPElementEntity *)mainElement;
|
||||||
- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context;
|
- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context;
|
||||||
|
@ -69,15 +69,7 @@
|
|||||||
|
|
||||||
- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context {
|
- (MPElementEntity *)elementInContext:(NSManagedObjectContext *)context {
|
||||||
|
|
||||||
if (!_elementOID)
|
return [MPElementEntity existingObjectWithID:_elementOID inContext:context];
|
||||||
return nil;
|
|
||||||
|
|
||||||
NSError *error = nil;
|
|
||||||
MPElementEntity *element = _elementOID? (MPElementEntity *)[context existingObjectWithID:_elementOID error:&error]: nil;
|
|
||||||
if (_elementOID && !element)
|
|
||||||
err(@"Failed to load element: %@", error);
|
|
||||||
|
|
||||||
return element;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reloadData {
|
- (void)reloadData {
|
||||||
|
@ -92,8 +92,17 @@
|
|||||||
|
|
||||||
[super reloadWithElement:mainElement];
|
[super reloadWithElement:mainElement];
|
||||||
|
|
||||||
|
if (!mainElement) {
|
||||||
|
self.loginButton.alpha = 0;
|
||||||
|
self.nameLabel.text = @"";
|
||||||
|
self.contentField.text = @"";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.nameLabel.alpha = 1;
|
||||||
self.loginButton.alpha = 1;
|
self.loginButton.alpha = 1;
|
||||||
self.typeLabel.text = [mainElement.algorithm nameOfType:self.type];
|
if (self.type != (MPElementType)NSNotFound)
|
||||||
|
self.typeLabel.text = [mainElement.algorithm nameOfType:self.type];
|
||||||
|
|
||||||
if (mainElement.requiresExplicitMigration)
|
if (mainElement.requiresExplicitMigration)
|
||||||
self.upgradeButton.alpha = 1;
|
self.upgradeButton.alpha = 1;
|
||||||
@ -166,6 +175,8 @@
|
|||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPElementEntity *element = [[MPPasswordElementCell findAsSuperviewOf:self] elementInContext:context];
|
MPElementEntity *element = [[MPPasswordElementCell findAsSuperviewOf:self] elementInContext:context];
|
||||||
|
if (!element)
|
||||||
|
return;
|
||||||
|
|
||||||
switch (self.contentFieldMode) {
|
switch (self.contentFieldMode) {
|
||||||
case MPContentFieldModePassword:
|
case MPContentFieldModePassword:
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#import "MPCell.h"
|
#import "MPCell.h"
|
||||||
#import "MPPasswordCell.h"
|
#import "MPPasswordCell.h"
|
||||||
#import "MPPasswordElementCell.h"
|
#import "MPPasswordElementCell.h"
|
||||||
|
#import "MPPasswordsViewController.h"
|
||||||
|
|
||||||
@interface MPPasswordTypesCell : MPPasswordElementCell <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
|
@interface MPPasswordTypesCell : MPPasswordElementCell <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
|
||||||
|
|
||||||
|
@ -68,9 +68,7 @@
|
|||||||
[super applyLayoutAttributes:layoutAttributes];
|
[super applyLayoutAttributes:layoutAttributes];
|
||||||
|
|
||||||
[self.contentCollectionView.collectionViewLayout invalidateLayout];
|
[self.contentCollectionView.collectionViewLayout invalidateLayout];
|
||||||
if (self.activeType)
|
[self scrollToActiveType];
|
||||||
[self.contentCollectionView scrollToItemAtIndexPath:[self contentIndexPathForType:self.activeType]
|
|
||||||
atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reloadWithTransientSite:(NSString *)siteName {
|
- (void)reloadWithTransientSite:(NSString *)siteName {
|
||||||
@ -104,7 +102,10 @@
|
|||||||
if (!self.algorithm)
|
if (!self.algorithm)
|
||||||
dbg_return_tr( 0, @, @(section) );
|
dbg_return_tr( 0, @, @(section) );
|
||||||
|
|
||||||
dbg_return_tr( [[self.algorithm allTypes] count] + 1, @, @(section) );
|
if (self.transientSite)
|
||||||
|
dbg_return_tr( [[self.algorithm allTypes] count], @, @(section) );
|
||||||
|
|
||||||
|
dbg_return_tr( [[self.algorithm allTypes] count] + 1 /* Delete */, @, @(section) );
|
||||||
}
|
}
|
||||||
|
|
||||||
- (MPPasswordLargeCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
- (MPPasswordLargeCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||||
@ -125,16 +126,13 @@
|
|||||||
|
|
||||||
NSString *newSiteName = self.transientSite;
|
NSString *newSiteName = self.transientSite;
|
||||||
if (newSiteName) {
|
if (newSiteName) {
|
||||||
|
[[UIResponder findFirstResponder] resignFirstResponder];
|
||||||
[PearlAlert showAlertWithTitle:@"Create Site"
|
[PearlAlert showAlertWithTitle:@"Create Site"
|
||||||
message:strf( @"Do you want to create a new site named:\n%@", newSiteName )
|
message:strf( @"Do you want to create a new site named:\n%@", newSiteName )
|
||||||
viewStyle:UIAlertViewStyleDefault
|
viewStyle:UIAlertViewStyleDefault
|
||||||
initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
|
initAlert:nil tappedButtonBlock:^(UIAlertView *alert, NSInteger buttonIndex) {
|
||||||
if (buttonIndex == [alert cancelButtonIndex]) {
|
if (buttonIndex == [alert cancelButtonIndex]) {
|
||||||
// Cancel
|
// Cancel
|
||||||
// NSIndexPath *indexPath_ = [collectionView indexPathForCell:cell];
|
|
||||||
// [collectionView selectItemAtIndexPath:indexPath animated:NO
|
|
||||||
// scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
|
|
||||||
// [collectionView deselectItemAtIndexPath:indexPath animated:YES];
|
|
||||||
for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems])
|
for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems])
|
||||||
[collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES];
|
[collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES];
|
||||||
return;
|
return;
|
||||||
@ -142,70 +140,56 @@
|
|||||||
|
|
||||||
// Create
|
// Create
|
||||||
[[MPiOSAppDelegate get] addElementNamed:newSiteName completion:^(MPElementEntity *element) {
|
[[MPiOSAppDelegate get] addElementNamed:newSiteName completion:^(MPElementEntity *element) {
|
||||||
|
[self copyContentOfElement:element];
|
||||||
PearlMainQueue( ^{
|
PearlMainQueue( ^{
|
||||||
[PearlOverlay showTemporaryOverlayWithTitle:strf( @"Added %@", newSiteName ) dismissAfter:2];
|
[self.passwordsViewController updatePasswords];
|
||||||
PearlMainQueueAfter( 0.2f, ^{
|
|
||||||
// NSIndexPath *indexPath_ = [collectionView indexPathForCell:cell];
|
|
||||||
// [collectionView selectItemAtIndexPath:indexPath animated:NO
|
|
||||||
// scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
|
|
||||||
// [collectionView deselectItemAtIndexPath:indexPath animated:YES];
|
|
||||||
for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems])
|
|
||||||
[collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES];
|
|
||||||
} );
|
|
||||||
} );
|
} );
|
||||||
}];
|
}];
|
||||||
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil];
|
} cancelTitle:[PearlStrings get].commonButtonCancel otherTitles:[PearlStrings get].commonButtonYes, nil];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MPElementEntity *element = [self mainElement];
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
if (!element) {
|
BOOL used = NO;
|
||||||
// [collectionView selectItemAtIndexPath:indexPath animated:NO
|
MPElementEntity *element = [self elementInContext:context];
|
||||||
// scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
|
if (!element)
|
||||||
// [collectionView deselectItemAtIndexPath:indexPath animated:YES];
|
wrn(@"No element to use for: %@", self);
|
||||||
for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems])
|
else if (indexPath.item == 0) {
|
||||||
[collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES];
|
[context deleteObject:element];
|
||||||
return;
|
[context saveToStore];
|
||||||
}
|
} else
|
||||||
|
used = [self copyContentOfElement:element];
|
||||||
|
|
||||||
|
PearlMainQueueAfter( 0.2f, ^{
|
||||||
|
for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems])
|
||||||
|
[collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES];
|
||||||
|
|
||||||
|
if (used)
|
||||||
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context_) {
|
||||||
|
[[self elementInContext:context_] use];
|
||||||
|
[context_ saveToStore];
|
||||||
|
}];
|
||||||
|
} );
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)copyContentOfElement:(MPElementEntity *)element {
|
||||||
|
|
||||||
inf( @"Copying password for: %@", element.name );
|
inf( @"Copying password for: %@", element.name );
|
||||||
MPCheckpoint( MPCheckpointCopyToPasteboard, @{
|
MPCheckpoint( MPCheckpointCopyToPasteboard, @{
|
||||||
@"type" : NilToNSNull( element.typeName ),
|
@"type" : NilToNSNull( element.typeName ),
|
||||||
@"version" : @(element.version),
|
@"version" : @(element.version),
|
||||||
@"emergency" : @NO
|
@"emergency" : @NO
|
||||||
} );
|
|
||||||
|
|
||||||
[element resolveContentUsingKey:[MPAppDelegate_Shared get].key result:^(NSString *result) {
|
|
||||||
if (![result length]) {
|
|
||||||
PearlMainQueue( ^{
|
|
||||||
// NSIndexPath *indexPath_ = [collectionView indexPathForCell:cell];
|
|
||||||
// [collectionView selectItemAtIndexPath:indexPath animated:NO
|
|
||||||
// scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
|
|
||||||
// [collectionView deselectItemAtIndexPath:indexPath animated:YES];
|
|
||||||
for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems])
|
|
||||||
[collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES];
|
|
||||||
} );
|
} );
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
NSString *result = [element resolveContentUsingKey:[MPAppDelegate_Shared get].key];
|
||||||
|
if ([result length]) {
|
||||||
[UIPasteboard generalPasteboard].string = result;
|
[UIPasteboard generalPasteboard].string = result;
|
||||||
PearlMainQueue( ^{
|
[PearlOverlay showTemporaryOverlayWithTitle:@"Password Copied" dismissAfter:2];
|
||||||
[PearlOverlay showTemporaryOverlayWithTitle:@"Password Copied" dismissAfter:2];
|
return YES;
|
||||||
PearlMainQueueAfter( 0.2f, ^{
|
}
|
||||||
// NSIndexPath *indexPath_ = [collectionView indexPathForCell:cell];
|
|
||||||
// [collectionView selectItemAtIndexPath:indexPath animated:NO
|
|
||||||
// scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
|
|
||||||
// [collectionView deselectItemAtIndexPath:indexPath animated:YES];
|
|
||||||
for (NSIndexPath *selectedIndexPath in [collectionView indexPathsForSelectedItems])
|
|
||||||
[collectionView deselectItemAtIndexPath:selectedIndexPath animated:YES];
|
|
||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
return NO;
|
||||||
[[self elementInContext:context] use];
|
|
||||||
[context saveToStore];
|
|
||||||
}];
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - UIScrollViewDelegate
|
#pragma mark - UIScrollViewDelegate
|
||||||
@ -235,8 +219,18 @@
|
|||||||
|
|
||||||
#pragma mark - Private
|
#pragma mark - Private
|
||||||
|
|
||||||
|
- (void)scrollToActiveType {
|
||||||
|
|
||||||
|
if (self.activeType && self.activeType != (MPElementType)NSNotFound)
|
||||||
|
[self.contentCollectionView scrollToItemAtIndexPath:[self contentIndexPathForType:self.activeType]
|
||||||
|
atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
|
||||||
|
}
|
||||||
|
|
||||||
- (MPElementType)typeForContentIndexPath:(NSIndexPath *)indexPath {
|
- (MPElementType)typeForContentIndexPath:(NSIndexPath *)indexPath {
|
||||||
|
|
||||||
|
if (self.transientSite)
|
||||||
|
return [[self.algorithm allTypesStartingWith:MPElementTypeGeneratedPIN][indexPath.item] unsignedIntegerValue];
|
||||||
|
|
||||||
if (indexPath.item == 0)
|
if (indexPath.item == 0)
|
||||||
return (MPElementType)NSNotFound;
|
return (MPElementType)NSNotFound;
|
||||||
|
|
||||||
@ -247,20 +241,29 @@
|
|||||||
|
|
||||||
NSArray *types = [self.algorithm allTypesStartingWith:MPElementTypeGeneratedPIN];
|
NSArray *types = [self.algorithm allTypesStartingWith:MPElementTypeGeneratedPIN];
|
||||||
for (NSInteger t = 0; t < [types count]; ++t)
|
for (NSInteger t = 0; t < [types count]; ++t)
|
||||||
if ([types[t] unsignedIntegerValue] == type)
|
if ([types[t] unsignedIntegerValue] == type) {
|
||||||
return [NSIndexPath indexPathForItem:t + 1 inSection:0];
|
if (self.transientSite)
|
||||||
|
return [NSIndexPath indexPathForItem:t inSection:0];
|
||||||
|
else
|
||||||
|
return [NSIndexPath indexPathForItem:t + 1 inSection:0];
|
||||||
|
}
|
||||||
|
|
||||||
Throw(@"Unsupported type: %d", type);
|
Throw(@"Unsupported type: %lud", (long)type);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)saveContentType {
|
- (void)saveContentType {
|
||||||
|
|
||||||
if (self.transientSite)
|
|
||||||
return;
|
|
||||||
|
|
||||||
CGPoint centerPoint = CGPointFromCGRectCenter( self.contentCollectionView.bounds );
|
CGPoint centerPoint = CGPointFromCGRectCenter( self.contentCollectionView.bounds );
|
||||||
NSIndexPath *centerIndexPath = [self.contentCollectionView indexPathForItemAtPoint:centerPoint];
|
NSIndexPath *centerIndexPath = [self.contentCollectionView indexPathForItemAtPoint:centerPoint];
|
||||||
self.activeType = [self typeForContentIndexPath:centerIndexPath];
|
MPElementType type = [self typeForContentIndexPath:centerIndexPath];
|
||||||
|
if (type == ((MPElementType)NSNotFound))
|
||||||
|
// Active cell is not a type cell.
|
||||||
|
return;
|
||||||
|
|
||||||
|
self.activeType = type;
|
||||||
|
|
||||||
|
if (self.transientSite)
|
||||||
|
return;
|
||||||
|
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPPasswordLargeCell *cell = (MPPasswordLargeCell *)[self.contentCollectionView cellForItemAtIndexPath:centerIndexPath];
|
MPPasswordLargeCell *cell = (MPPasswordLargeCell *)[self.contentCollectionView cellForItemAtIndexPath:centerIndexPath];
|
||||||
@ -270,7 +273,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
MPElementEntity *element = [self elementInContext:context];
|
MPElementEntity *element = [self elementInContext:context];
|
||||||
if (element.type == cell.type)
|
if (!element || element.type == cell.type)
|
||||||
// Nothing changed.
|
// Nothing changed.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -284,8 +287,7 @@
|
|||||||
|
|
||||||
_activeType = activeType;
|
_activeType = activeType;
|
||||||
|
|
||||||
[self.contentCollectionView scrollToItemAtIndexPath:[self contentIndexPathForType:activeType]
|
[self scrollToActiveType];
|
||||||
atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSelected:(BOOL)selected {
|
- (void)setSelected:(BOOL)selected {
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
@property(nonatomic, readonly) MPCoachmark *coachmark;
|
@property(nonatomic, readonly) MPCoachmark *coachmark;
|
||||||
|
|
||||||
- (void)setActive:(BOOL)active animated:(BOOL)animated completion:(void (^)(BOOL finished))completion;
|
- (void)setActive:(BOOL)active animated:(BOOL)animated completion:(void (^)(BOOL finished))completion;
|
||||||
|
- (void)updatePasswords;
|
||||||
|
|
||||||
- (IBAction)dismissPopdown:(id)sender;
|
- (IBAction)dismissPopdown:(id)sender;
|
||||||
- (IBAction)signOut:(id)sender;
|
- (IBAction)signOut:(id)sender;
|
||||||
|
@ -40,7 +40,6 @@
|
|||||||
__weak UITapGestureRecognizer *_passwordsDismissRecognizer;
|
__weak UITapGestureRecognizer *_passwordsDismissRecognizer;
|
||||||
NSFetchedResultsController *_fetchedResultsController;
|
NSFetchedResultsController *_fetchedResultsController;
|
||||||
BOOL _exactMatch;
|
BOOL _exactMatch;
|
||||||
NSMutableDictionary *_fetchedUpdates;
|
|
||||||
UIColor *_backgroundColor;
|
UIColor *_backgroundColor;
|
||||||
UIColor *_darkenedBackgroundColor;
|
UIColor *_darkenedBackgroundColor;
|
||||||
__weak UIViewController *_popdownVC;
|
__weak UIViewController *_popdownVC;
|
||||||
@ -52,7 +51,6 @@
|
|||||||
|
|
||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
|
|
||||||
_fetchedUpdates = [NSMutableDictionary dictionaryWithCapacity:4];
|
|
||||||
_backgroundColor = self.passwordCollectionView.backgroundColor;
|
_backgroundColor = self.passwordCollectionView.backgroundColor;
|
||||||
_darkenedBackgroundColor = [_backgroundColor colorWithAlphaComponent:0.6f];
|
_darkenedBackgroundColor = [_backgroundColor colorWithAlphaComponent:0.6f];
|
||||||
_coachmark = [MPCoachmark coachmarkForClass:[self class] version:0];
|
_coachmark = [MPCoachmark coachmarkForClass:[self class] version:0];
|
||||||
@ -118,11 +116,7 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
if (collectionView == self.passwordCollectionView) {
|
if (collectionView == self.passwordCollectionView) {
|
||||||
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout;
|
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)collectionViewLayout;
|
||||||
CGFloat itemWidth = UIEdgeInsetsInsetRect(self.passwordCollectionView.bounds, layout.sectionInset).size.width;
|
CGFloat itemWidth = UIEdgeInsetsInsetRect(self.passwordCollectionView.bounds, layout.sectionInset).size.width;
|
||||||
|
return CGSizeMake( itemWidth, 100 );
|
||||||
if (indexPath.item < 3 || indexPath.item >= ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects)
|
|
||||||
return CGSizeMake( itemWidth, 100 );
|
|
||||||
|
|
||||||
return CGSizeMake( (itemWidth - layout.minimumInteritemSpacing) / 2, 44 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Throw(@"Unexpected collection view: %@", collectionView);
|
Throw(@"Unexpected collection view: %@", collectionView);
|
||||||
@ -155,14 +149,12 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
MPPasswordElementCell *cell;
|
MPPasswordElementCell *cell;
|
||||||
if (indexPath.item < ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects) {
|
if (indexPath.item < ((id<NSFetchedResultsSectionInfo>)self.fetchedResultsController.sections[indexPath.section]).numberOfObjects) {
|
||||||
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
|
MPElementEntity *element = [self.fetchedResultsController objectAtIndexPath:indexPath];
|
||||||
if (indexPath.item < 3)
|
cell = [MPPasswordTypesCell dequeueCellForElement:element fromCollectionView:collectionView atIndexPath:indexPath];
|
||||||
cell = [MPPasswordTypesCell dequeueCellForElement:element fromCollectionView:collectionView atIndexPath:indexPath];
|
|
||||||
else
|
|
||||||
cell = [MPPasswordSmallCell dequeueCellForElement:element fromCollectionView:collectionView atIndexPath:indexPath];
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
// New Site.
|
// New Site.
|
||||||
cell = [MPPasswordTypesCell dequeueCellForTransientSite:self.query fromCollectionView:collectionView atIndexPath:indexPath];
|
cell = [MPPasswordTypesCell dequeueCellForTransientSite:self.query fromCollectionView:collectionView atIndexPath:indexPath];
|
||||||
|
cell.passwordsViewController = self;
|
||||||
|
|
||||||
[UIView setAnimationsEnabled:YES];
|
[UIView setAnimationsEnabled:YES];
|
||||||
dbg_return(cell, indexPath);
|
dbg_return(cell, indexPath);
|
||||||
@ -171,140 +163,30 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
Throw(@"Unexpected collection view: %@", collectionView);
|
Throw(@"Unexpected collection view: %@", collectionView);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - NSFetchedResultsControllerDelegate
|
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind
|
||||||
|
atIndexPath:(NSIndexPath *)indexPath {
|
||||||
|
|
||||||
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
|
return [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"MPPasswordHeader" forIndexPath:indexPath];
|
||||||
|
|
||||||
if (controller == _fetchedResultsController) {
|
|
||||||
dbg(@"controllerWillChangeContent");
|
|
||||||
NSAssert(![_fetchedUpdates count], @"Didn't finish a previous change update?");
|
|
||||||
if ([_fetchedUpdates count]) {
|
|
||||||
[_fetchedUpdates removeAllObjects];
|
|
||||||
[self.passwordCollectionView reloadData];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSFetchedResultsControllerDelegate
|
||||||
|
|
||||||
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath
|
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath
|
||||||
forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
|
forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
|
||||||
|
|
||||||
if (controller == _fetchedResultsController) {
|
if (controller == _fetchedResultsController) {
|
||||||
NSMutableArray *updatesForType = _fetchedUpdates[@(type)];
|
[self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section]];
|
||||||
if (!updatesForType)
|
[self.passwordCollectionView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section]];
|
||||||
_fetchedUpdates[@(type)] = updatesForType = [NSMutableArray new];
|
|
||||||
|
|
||||||
[updatesForType addObject:@{
|
|
||||||
@"object" : NilToNSNull(anObject),
|
|
||||||
@"indexPath" : NilToNSNull(indexPath),
|
|
||||||
@"newIndexPath" : NilToNSNull(newIndexPath)
|
|
||||||
}];
|
|
||||||
switch (type) {
|
|
||||||
case NSFetchedResultsChangeInsert:
|
|
||||||
dbg(@"didChangeObject: insert: %@", [updatesForType lastObject]);
|
|
||||||
break;
|
|
||||||
case NSFetchedResultsChangeDelete:
|
|
||||||
dbg(@"didChangeObject: delete: %@", [updatesForType lastObject]);
|
|
||||||
break;
|
|
||||||
case NSFetchedResultsChangeMove:
|
|
||||||
dbg(@"didChangeObject: move: %@", [updatesForType lastObject]);
|
|
||||||
break;
|
|
||||||
case NSFetchedResultsChangeUpdate:
|
|
||||||
dbg(@"didChangeObject: update: %@", [updatesForType lastObject]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo
|
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo
|
||||||
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
|
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
|
||||||
|
|
||||||
if (controller == _fetchedResultsController) {
|
if (controller == _fetchedResultsController)
|
||||||
NSMutableArray *updatesForType = _fetchedUpdates[@(type << 3)];
|
[self.passwordCollectionView reloadData];
|
||||||
if (!updatesForType)
|
|
||||||
_fetchedUpdates[@(type << 3)] = updatesForType = [NSMutableArray new];
|
|
||||||
|
|
||||||
[updatesForType addObject:@{
|
|
||||||
@"sectionInfo" : NilToNSNull(sectionInfo),
|
|
||||||
@"index" : @(sectionIndex)
|
|
||||||
}];
|
|
||||||
switch (type) {
|
|
||||||
case NSFetchedResultsChangeInsert:
|
|
||||||
dbg(@"didChangeSection: insert: %@", [updatesForType lastObject]);
|
|
||||||
break;
|
|
||||||
case NSFetchedResultsChangeDelete:
|
|
||||||
dbg(@"didChangeSection: delete: %@", [updatesForType lastObject]);
|
|
||||||
break;
|
|
||||||
case NSFetchedResultsChangeMove:
|
|
||||||
dbg(@"didChangeSection: move: %@", [updatesForType lastObject]);
|
|
||||||
break;
|
|
||||||
case NSFetchedResultsChangeUpdate:
|
|
||||||
dbg(@"didChangeSection: update: %@", [updatesForType lastObject]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
|
|
||||||
|
|
||||||
if (controller == _fetchedResultsController && [_fetchedUpdates count]) {
|
|
||||||
[self.passwordCollectionView performBatchUpdates:^{
|
|
||||||
[_fetchedUpdates enumerateKeysAndObjectsUsingBlock:^(NSNumber *typeNumber, NSArray *updates, BOOL *stop) {
|
|
||||||
BOOL updateIsSection = NO;
|
|
||||||
NSFetchedResultsChangeType type = [typeNumber unsignedIntegerValue];
|
|
||||||
if (type >= 1 << 3) {
|
|
||||||
updateIsSection = YES;
|
|
||||||
type = type >> 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case NSFetchedResultsChangeInsert:
|
|
||||||
if (updateIsSection) {
|
|
||||||
for (NSDictionary *update in updates) {
|
|
||||||
dbg(@"insertSections:%@", update[@"index"]);
|
|
||||||
[self.passwordCollectionView insertSections:
|
|
||||||
[NSIndexSet indexSetWithIndex:[update[@"index"] unsignedIntegerValue]]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
dbg(@"insertItemsAtIndexPaths:%@", [updates valueForKeyPath:@"@unionOfObjects.newIndexPath"]);
|
|
||||||
[self.passwordCollectionView insertItemsAtIndexPaths:[updates valueForKeyPath:@"@unionOfObjects.newIndexPath"]];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NSFetchedResultsChangeDelete:
|
|
||||||
if (updateIsSection) {
|
|
||||||
for (NSDictionary *update in updates) {
|
|
||||||
dbg(@"deleteSections:%@", update[@"index"]);
|
|
||||||
[self.passwordCollectionView deleteSections:
|
|
||||||
[NSIndexSet indexSetWithIndex:[update[@"index"] unsignedIntegerValue]]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
dbg(@"deleteItemsAtIndexPaths:%@", [updates valueForKeyPath:@"@unionOfObjects.indexPath"]);
|
|
||||||
[self.passwordCollectionView deleteItemsAtIndexPaths:[updates valueForKeyPath:@"@unionOfObjects.indexPath"]];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NSFetchedResultsChangeMove:
|
|
||||||
NSAssert(!updateIsSection, @"Move not supported for sections");
|
|
||||||
for (NSDictionary *update in updates) {
|
|
||||||
dbg(@"moveItemAtIndexPath:%@ toIndexPath:%@", update[@"indexPath"], update[@"newIndexPath"]);
|
|
||||||
[self.passwordCollectionView moveItemAtIndexPath:update[@"indexPath"] toIndexPath:update[@"newIndexPath"]];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NSFetchedResultsChangeUpdate:
|
|
||||||
NSAssert(!updateIsSection, @"Update not supported for sections");
|
|
||||||
dbg(@"reloadItemsAtIndexPaths:%@", [updates valueForKeyPath:@"@unionOfObjects.indexPath"]);
|
|
||||||
[self.passwordCollectionView reloadItemsAtIndexPaths:[updates valueForKeyPath:@"@unionOfObjects.indexPath"]];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
} completion:nil];
|
|
||||||
[_fetchedUpdates removeAllObjects];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - UIScrollViewDelegate
|
|
||||||
|
|
||||||
#pragma mark - UISearchBarDelegate
|
#pragma mark - UISearchBarDelegate
|
||||||
|
|
||||||
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
|
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
|
||||||
@ -319,13 +201,10 @@ referenceSizeForHeaderInSection:(NSInteger)section {
|
|||||||
|
|
||||||
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
|
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
|
||||||
|
|
||||||
if (searchBar == self.passwordsSearchBar) {
|
if (searchBar == self.passwordsSearchBar)
|
||||||
// _passwordsDismissRecognizer = [self.view dismissKeyboardForField:self.passwordsSearchBar onTouchForced:NO];
|
|
||||||
|
|
||||||
[UIView animateWithDuration:0.3f animations:^{
|
[UIView animateWithDuration:0.3f animations:^{
|
||||||
self.passwordCollectionView.backgroundColor = _darkenedBackgroundColor;
|
self.passwordCollectionView.backgroundColor = _darkenedBackgroundColor;
|
||||||
}];
|
}];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
|
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
|
||||||
|
@ -360,28 +360,31 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
if (buttonIndex == [sheet destructiveButtonIndex]) {
|
if (buttonIndex == [sheet destructiveButtonIndex]) {
|
||||||
// Delete User
|
// Delete User
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
NSManagedObject *user_ = [context existingObjectWithID:userID error:NULL];
|
MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
|
||||||
if (user_) {
|
if (!user_)
|
||||||
[context deleteObject:user_];
|
return;
|
||||||
[context saveToStore];
|
|
||||||
}
|
[context deleteObject:user_];
|
||||||
|
[context saveToStore];
|
||||||
}];
|
}];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buttonIndex == [sheet firstOtherButtonIndex])
|
if (buttonIndex == [sheet firstOtherButtonIndex])
|
||||||
// Reset Password
|
// Reset Password
|
||||||
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
|
||||||
MPUserEntity *user_ = (MPUserEntity *)[context existingObjectWithID:userID error:NULL];
|
MPUserEntity *user_ = [MPUserEntity existingObjectWithID:userID inContext:context];
|
||||||
if (user_)
|
if (!user_)
|
||||||
[[MPiOSAppDelegate get] changeMasterPasswordFor:user_ saveInContext:context didResetBlock:^{
|
return;
|
||||||
PearlMainQueue( ^{
|
|
||||||
NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForCell:avatarCell];
|
[[MPiOSAppDelegate get] changeMasterPasswordFor:user_ saveInContext:context didResetBlock:^{
|
||||||
[self.avatarCollectionView selectItemAtIndexPath:avatarIndexPath animated:NO
|
PearlMainQueue( ^{
|
||||||
scrollPosition:UICollectionViewScrollPositionNone];
|
NSIndexPath *avatarIndexPath = [self.avatarCollectionView indexPathForCell:avatarCell];
|
||||||
[self collectionView:self.avatarCollectionView didSelectItemAtIndexPath:avatarIndexPath];
|
[self.avatarCollectionView selectItemAtIndexPath:avatarIndexPath animated:NO
|
||||||
} );
|
scrollPosition:UICollectionViewScrollPositionNone];
|
||||||
}];
|
[self collectionView:self.avatarCollectionView didSelectItemAtIndexPath:avatarIndexPath];
|
||||||
|
} );
|
||||||
|
}];
|
||||||
}];
|
}];
|
||||||
} cancelTitle:[PearlStrings get].commonButtonCancel
|
} cancelTitle:[PearlStrings get].commonButtonCancel
|
||||||
destructiveTitle:@"Delete User" otherTitles:@"Reset Password", nil];
|
destructiveTitle:@"Delete User" otherTitles:@"Reset Password", nil];
|
||||||
@ -477,12 +480,7 @@ typedef NS_ENUM(NSUInteger, MPActiveUserState) {
|
|||||||
if ((*isNew = indexPath.item >= [self.userIDs count]))
|
if ((*isNew = indexPath.item >= [self.userIDs count]))
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
NSError *error = nil;
|
return [MPUserEntity existingObjectWithID:self.userIDs[indexPath.item] inContext:context];
|
||||||
MPUserEntity *user = (MPUserEntity *)[context existingObjectWithID:self.userIDs[indexPath.item] error:&error];
|
|
||||||
if (error)
|
|
||||||
wrn(@"Failed to load user into context: %@", error);
|
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateAvatars {
|
- (void)updateAvatars {
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="504"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="504"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rWM-08-aab" userLabel="Users Root">
|
<view contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rWM-08-aab" userLabel="Users Root">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="504"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="504"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
@ -27,7 +27,7 @@
|
|||||||
<rect key="frame" x="142" y="234" width="37" height="37"/>
|
<rect key="frame" x="142" y="234" width="37" height="37"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
</activityIndicatorView>
|
</activityIndicatorView>
|
||||||
<collectionView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" alpha="0.0" contentMode="scaleToFill" minimumZoomScale="0.0" maximumZoomScale="0.0" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="L6J-pd-gcp" userLabel="Avatar Collection">
|
<collectionView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" alpha="0.0" contentMode="scaleToFill" misplaced="YES" minimumZoomScale="0.0" maximumZoomScale="0.0" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="L6J-pd-gcp" userLabel="Avatar Collection">
|
||||||
<rect key="frame" x="0.0" y="20" width="320" height="484"/>
|
<rect key="frame" x="0.0" y="20" width="320" height="484"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||||
@ -122,7 +122,7 @@
|
|||||||
<outlet property="delegate" destination="S8q-YF-Kt9" id="det-Eh-phM"/>
|
<outlet property="delegate" destination="S8q-YF-Kt9" id="det-Eh-phM"/>
|
||||||
</connections>
|
</connections>
|
||||||
</collectionView>
|
</collectionView>
|
||||||
<button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9u7-pu-Wtv" userLabel="Previous Avatar">
|
<button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9u7-pu-Wtv" userLabel="Previous Avatar">
|
||||||
<rect key="frame" x="0.0" y="127" width="44" height="53"/>
|
<rect key="frame" x="0.0" y="127" width="44" height="53"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
@ -136,7 +136,7 @@
|
|||||||
<action selector="changeAvatar:" destination="S8q-YF-Kt9" eventType="touchUpInside" id="lNu-mK-3zD"/>
|
<action selector="changeAvatar:" destination="S8q-YF-Kt9" eventType="touchUpInside" id="lNu-mK-3zD"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fUK-gJ-NRE" userLabel="Next Avatar">
|
<button opaque="NO" alpha="0.69999999999999996" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fUK-gJ-NRE" userLabel="Next Avatar">
|
||||||
<rect key="frame" x="276" y="127" width="44" height="53"/>
|
<rect key="frame" x="276" y="127" width="44" height="53"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
@ -150,7 +150,7 @@
|
|||||||
<action selector="changeAvatar:" destination="S8q-YF-Kt9" eventType="touchUpInside" id="kL5-zV-zbb"/>
|
<action selector="changeAvatar:" destination="S8q-YF-Kt9" eventType="touchUpInside" id="kL5-zV-zbb"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qp1-nX-o4i" userLabel="Entry" customClass="PearlUIView">
|
<view contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qp1-nX-o4i" userLabel="Entry" customClass="PearlUIView">
|
||||||
<rect key="frame" x="20" y="216" width="280" height="63"/>
|
<rect key="frame" x="20" y="216" width="280" height="63"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
@ -1307,7 +1307,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</collectionViewCell>
|
</collectionViewCell>
|
||||||
</cells>
|
</cells>
|
||||||
<collectionReusableView key="sectionHeaderView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="yzh-hh-YjZ">
|
<collectionReusableView key="sectionHeaderView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MPPasswordHeader" id="yzh-hh-YjZ">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="108"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="108"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</collectionReusableView>
|
</collectionReusableView>
|
||||||
|
Loading…
Reference in New Issue
Block a user