iOS開源加密相簿Agony的實現(三)

來源:互聯網
上載者:User

標籤:

簡介

雖然目前市面上有一些不錯的加密相簿App,但不是內建廣告,就是對上傳的張數有所限制。本文介紹了一個加密相簿的製作過程,該加密相簿將包括多密碼(輸入不同的密碼即可訪問不同的空間,可掩人耳目)、WiFi傳圖、照片檔案加密等功能。目前項目和文章會同時前進,項目的原始碼可以在github上下載。
點擊前往GitHub

概述

上一篇文章主要介紹了登入與註冊頁面的設計。這一篇文章將會介紹相簿的設計與實現。

相簿設計與實現互動設計

相簿的主介面如下。

點擊Add按鈕可以添加一個相簿檔案夾,通過彈出的AlertView來命名,介面如下。

長按一個已有的相簿可以進行刪除操作,介面如下。

檔案結構

相簿使用了MVC設計模式,為了方便排布,使用了UICollectionView,檔案結構如下。

模型類設計

模型類為SGAlbum,每個模型對應一個相簿,儲存相簿的名字、儲存路徑,封面圖路徑,注意到前面的頁面設計中添加相簿的按鈕也作為一個Cell存在,因此在模型類中有一個屬性描述是否是添加相簿按鈕。綜上所述,設計如下。

typedef NS_OPTIONS(NSInteger, SGAlbumType) {    SGAlbumButtonTypeCommon = 0,    SGAlbumButtonTypeAddButton};@interface SGAlbum : NSObject@property (nonatomic, copy) NSString *name;@property (nonatomic, copy) NSString *path;@property (nonatomic, copy) NSString *coverImageURL;@property (nonatomic, assign) SGAlbumType type;@end
Cell設計

每個模型被傳遞到collectionView的Cell並且顯示出來,每個Cell都是一個UICollectionCell的子類,它的子視圖包括了背景圖、相簿封面圖、相簿名稱標籤三個部分,如下。

@interface SGHomeViewCell ()@property (nonatomic, weak) UIImageView *backgroundImageView;@property (nonatomic, weak) UIImageView *thumbImageView;@property (nonatomic, weak) UILabel *nameLabel;@end

為了處理長按刪除這一事件,為Cell的contentView添加一個LongPress事件,而最後該事件會交給collectionView處理,collectionView接收到訊息後顯示UIActionSheet來讓使用者確認操作,操作被確認後選擇的相簿檔案夾將會被刪除,同時訊息會繼續傳遞到控制器,來重新負載檔案,這兩次訊息傳遞均通過block完成,在Cell上提供了一個block來回調到collectionView,由於直接對block賦值無法獲得智能補全提示,因此將block作為私人屬性,寫一個單獨的setter來設定回調,代碼如下。

// block setter- (void)setAction:(SGHomeViewCellActionBlock)actionBlock;// private block property@property (nonatomic, copy) SGHomeViewCellActionBlock actionBlock;// add gestureUILongPressGestureRecognizer *press = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(press:)];press.minimumPressDuration = 0.5f;[self.contentView addGestureRecognizer:press];// gesture handler- (void)press:(UILongPressGestureRecognizer *)ges {    // 第一個相簿為添加按鈕,不用處理長按刪除事件    if (ges.state != UIGestureRecognizerStateBegan) return;    if (self.album.type == SGAlbumButtonTypeCommon) {        if (self.actionBlock) {            self.actionBlock();        }    }}

通過重寫setter來監聽模型的傳遞,當模型被傳遞到Cell時,使用模型中的資料去渲染視圖,如果沒有提供封面圖,則使用預設封面,代碼如下。

- (void)setAlbum:(SGAlbum *)album {    _album = album;    if (album.type == SGAlbumButtonTypeAddButton) {        self.thumbImageView.image = [UIImage imageNamed:@"AlbumAddButton"];        self.nameLabel.text = @"Add";    } else {        UIImage *thumb = [UIImage imageNamed:album.coverImageURL ?: @"AlbumCover_placeholder"];        self.thumbImageView.image = thumb;        self.nameLabel.text = album.name;    }}

為了減少collectionView的代碼量,將Cell的建立和複用的邏輯放到Cell中。代碼如下。

+ (instancetype)cellWithCollectionView:(UICollectionView *)collectionView forIndexPath:(NSIndexPath *)indexPath {    static NSString *ID = @"SGHomeViewCell";    // register方法保證了dequeue方法無可複用時建立一個新的Cell    [collectionView registerClass:[SGHomeViewCell class] forCellWithReuseIdentifier:ID];    SGHomeViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];    return cell;}
CollectionView設計

collectionView以自身作為資料來源,同時負責處理Cell長按事件,刪除相簿並將事件繼續傳遞到控制器來重新負載檔案,資料來源需要的資料由控制器載入沙箱檔案來提供,綜上所述,collectionView需要的屬性如下。

@interface SGHomeView : UICollectionView// 資料來源需要的資料,由控制器負責載入@property (nonatomic, strong) NSArray<SGAlbum *> *albums;// 用於向控制器二次傳遞Cell的長按事件,處理方式與Cell設計中一樣- (void)setAction:(SGHomeViewNeedReloadActionBlock)actionBlock;@end

collectionView在資料來源擷取Cell時來設計block回調,代碼如下。

- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {    SGHomeViewCell *cell = [SGHomeViewCell cellWithCollectionView:collectionView forIndexPath:indexPath];    SGAlbum *album = self.albums[indexPath.row];    cell.album = album;    WS(); // 建立weakSelf的宏,防止循環參考    [cell setAction:^{        UIActionSheet *ac = [[UIActionSheet alloc] initWithTitle:@"Operation" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete" otherButtonTitles:nil];        [ac showInView:self.superview];        // currentSelectAlbum用於記錄當前相簿模型,以便actionSheet的回調裡刪除對應的相簿。        weakSelf.currentSelectAlbum = album;    }];    return cell;}

由於相簿模型已經提供了儲存路徑,因此刪除十分方便,刪除後通過block將事件繼續會傳到控制器,代碼如下。

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {    if(buttonIndex == 0) {        [[NSFileManager defaultManager] removeItemAtPath:self.currentSelectAlbum.path error:nil];        if (self.actionBlock) {            self.actionBlock();        }    }}
控制器設計檔案系統設計

控制器主要負責處理檔案的載入,相簿在沙箱中的儲存如下所述。
相簿的根目錄為Documents目錄,此目錄的檔案可以被iCloud備份以防止資料丟失。 為了區分不同密碼對應的不同賬戶,以每個密碼加密後的名稱作為檔案夾名稱,這些檔案夾作為不同賬戶的相簿根目錄。每個賬戶下建立的相簿都會以實體檔案夾的形式存在於各自的根目錄下。同時每個相簿中分Photo和Thumb目錄來儲存原圖和縮圖,一個檔案結構的例子如所示。
被高亮的行是某個賬戶的根目錄

由於涉及了較多的檔案路徑,因此使用一個工具類來管理這些路徑,稱為SGFileUtil,該類根據賬戶對象來初始化一個賬戶對應的根目錄,本節中用到的屬性和方法如下。

@interface SGFileUtil : NSObject@property (nonatomic, strong) SGAccount *account;@property (nonatomic, copy, readonly) NSString *rootPath;+ (instancetype)sharedUtil;@end

通過重寫account的setter實現rootPath根據當前account來變化,如果rootPath這一檔案夾不存在,則建立出來,代碼如下。

- (void)setAccount:(SGAccount *)account {    _account = account;    _rootPath = [DocumentPath stringByAppendingPathComponent:account.password];    NSFileManager *mgr = [NSFileManager defaultManager];    if (![mgr fileExistsAtPath:_rootPath isDirectory:nil]) {        [mgr createDirectoryAtPath:_rootPath withIntermediateDirectories:NO attributes:nil error:nil];    }}
控制器的商務邏輯

在視圖載入時,首先將collectionView添加到控制器視圖上,並且設定collectionView的block回調,用於處理Cell長按事件回傳。接下來要處理檔案的載入。

- (void)viewDidLoad {    [super viewDidLoad];    [self setupView];    [self loadFiles];}- (void)setupView {    self.title = @"Agony";    UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new];    [layout setScrollDirection:UICollectionViewScrollDirectionVertical];    SGHomeView *view = [[SGHomeView alloc] initWithFrame:(CGRect){0, 0, [UIScreen mainScreen].bounds.size} collectionViewLayout:layout];    view.alwaysBounceVertical = YES;    view.delegate = self;    WS(); // 用於建立weakSelf的宏,防止循環參考    [view setAction:^{        [weakSelf loadFiles];    }];    self.homeView = view;    [self.view addSubview:view];}- (void)loadFiles {    SGFileUtil *util = [SGFileUtil sharedUtil];    NSString *rootPath = util.rootPath;    NSFileManager *mgr = [NSFileManager defaultManager];    NSMutableArray<SGAlbum *> *albums = @[].mutableCopy;    // 第一個相簿為添加相簿按鈕,手動添加    SGAlbum *addBtnAlbum = [SGAlbum new];    addBtnAlbum.type = SGAlbumButtonTypeAddButton;    [albums addObject:addBtnAlbum];    // 其他相簿從當前賬戶的根目錄搜尋    NSArray *fileNames = [mgr contentsOfDirectoryAtPath:rootPath error:nil];    for (NSUInteger i = 0; i < fileNames.count; i++) {        NSString *fileName = fileNames[i];        SGAlbum *album = [SGAlbum new];        album.name = fileName;        album.path = [[SGFileUtil sharedUtil].rootPath stringByAppendingPathComponent:fileName];        [albums addObject:album];    }    self.homeView.albums = albums;    [self.homeView reloadData];}

控制器同時作為collectionView的代理,處理尺寸與點擊事件,代碼如下。

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {    return CGSizeMake(100, 100);}- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {    return UIEdgeInsetsMake(5, 10, 5, 10);}- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {    SGAlbum *album = self.homeView.albums[indexPath.row];    // 如果是添加相簿按鈕,則彈出alertView來處理建立相簿事件    // 否則啟動照片瀏覽器,來顯示相簿內容,照片瀏覽器在下一篇文章介紹    if (album.type == SGAlbumButtonTypeAddButton) {        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"New Folder" message:@"Please enter folder name" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil];        alertView.alertViewStyle = UIAlertViewStylePlainTextInput;        [alertView show];    } else {        SGPhotoBrowserViewController *browser = [SGPhotoBrowserViewController new];        browser.rootPath = album.path;        [self.navigationController pushViewController:browser animated:YES];    }}

在alertView的回調中處理檔案夾建立,檔案夾名不可為空,並且不能重複,檢查完畢後建立檔案夾,並且重新負載檔案。

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {    if (buttonIndex == 1) {        NSString *folderName = [alertView textFieldAtIndex:0].text;        if (!folderName.length) {            [MBProgressHUD showError:@"Folder Name Cannot be Empty"];            return;        }        NSFileManager *mgr = [NSFileManager defaultManager];        NSString *folderPath = [[SGFileUtil sharedUtil].rootPath stringByAppendingPathComponent:folderName];        if ([mgr fileExistsAtPath:folderPath isDirectory:nil]) {            [MBProgressHUD showError:@"Folder Exists"];            return;        }        [mgr createDirectoryAtPath:folderPath withIntermediateDirectories:NO attributes:nil error:nil];        [self loadFiles];    }}
總結

本文主要介紹了相簿頁面的設計與實現,目前項目已經完成了圖片瀏覽器,歡迎關注項目後續,項目的可以在文章開頭找到。

iOS開源加密相簿Agony的實現(三)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.