iOS中寫一個仿Snapseed的ImagePickerController(照片選擇器)

ImagePickerController(圖片選擇器)是iOS開發(fā)中一個很常用的UI控件。作為攝影愛好者婆廊,而Google出品的攝影后期App,Snapseed中那個圖片選擇器,好看又實用锡足,便嘗試著仿寫了一個忱屑。集成后一行代碼即可實現(xiàn)下圖效果蹬敲。



實現(xiàn)原理從界面說起。
界面可以分解為一個背景圖層用于點擊關(guān)閉(空白處點擊消失)莺戒,一個橫向可滾動的collectionview用于顯示圖庫里的圖片伴嗡,一個tableview用于存放功能按鈕。

1. 功能按鈕的tableview

先從簡單的tableview說起从铲,從小模塊到大模塊應該是以下方面瘪校。

  • 自定義數(shù)組數(shù)據(jù)對象item方便調(diào)整按鈕的文字圖片數(shù)量功能
  • 自定義tableview的cell方便樣式調(diào)整,cell中屬性根據(jù)對應item設置
  • 自定義tableview的datasourece在之中設置cell和item的對應關(guān)系
  • tableview的deleagte名段,設置點擊事件
  • 點擊功能實現(xiàn)阱扬,設置UIImagePickerController
  • tableview的view初始化,設置datasource伸辟,view繪制

自定義item對象麻惶,item對象定義了action的顯示名字,類型信夫,圖片

@property(nonatomic, copy) NSString *actionTitle;
@property(nonatomic, strong) UIImage *actionImage;
@property(nonatomic, assign) CDZImagePickerActionType actionType;

其中CDZImagePickerActionType可用來定義按鈕動作類型窃蹋,可以用枚舉來定義

typedef NS_ENUM(NSInteger, CDZImagePickerActionType) {
    CDZImagePickerCameraAction,
    CDZImagePickerLibraryAction,
    CDZImagePickerRecentAction,
    CDZImagePickerCloseAction
};

定義一個init方法用來快捷初始化item對象

- (instancetype)initWithTitle:(NSString *)titele withActionType:(CDZImagePickerActionType)type withImage:(UIImage *)image{
    self = [super init];
    if (self) {
        _actionTitle = titele;
        _actionType = type;
        _actionImage = image;
    }
    return self;
}

自定義cell對象卡啰,封裝一個方法來對應item和cell的關(guān)系

- (void)setCellFromItem:(CDZImagePickerActionsItem *)item{
    self.textLabel.text = item.actionTitle;
    self.imageView.image = item.actionImage;
}

可以重寫layoutSubviews方法達到cell中文字,圖片樣式自定義的效果警没,比如可以寫有圖片和無圖片的樣式布局

- (void)layoutSubviews{
    [super layoutSubviews];
    self.selectionStyle = UITableViewCellSelectionStyleNone;//點擊不變色
    self.imageView.frame = CGRectMake(20, 15, 22, 22);
    self.imageView.contentMode = UIViewContentModeScaleAspectFit;
    self.textLabel.font = [UIFont systemFontOfSize:16.0f];
    self.textLabel.textColor = [UIColor colorWithRed:0.30 green:0.30 blue:0.30 alpha:1.00];
    if (self.imageView.image){
        self.textLabel.frame = CGRectMake(60, 2, 200, 48);
        self.textLabel.textAlignment = NSTextAlignmentLeft;
    }
    else{
        self.textLabel.textAlignment = NSTextAlignmentCenter;
    }
}

自定義datasource匈辱,這一步可以根據(jù)個人選擇,個人習慣是把datasource從viewcontroller里抽離杀迹,可以讓controller更少代碼,datasource有數(shù)組對象用于存放item

@property (nonatomic, strong) NSMutableArray *itemArray;
實現(xiàn)datasource方法

#pragma mark - tableViewDataSourceRequried
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return self.itemArray.count;
}
        

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    CDZImagePickerActionsItem *item = self.itemArray[indexPath.row];
    CDZImagePickerActionsCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([CDZImagePickerActionsCell class])];
    if (!cell) {
        cell = [[CDZImagePickerActionsCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NSStringFromClass([CDZImagePickerActionsCell class])];
    }
    [cell setCellFromItem:item];//將cell和item對應起來
    return cell;
}

實現(xiàn)delegate和點擊事件

#pragma mark - tableViewDelegate

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return actionsViewCellHeight;
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    CDZImagePickerActionsItem *item = self.actionArray[indexPath.row];
    [self doActionsWithType:item.actionType];
}
#pragma mark - actions
- (void)doActionsWithType:(CDZImagePickerActionType)type{
    switch (type) {
        case  CDZImagePickerCameraAction:
            [self openCamera];
            break;
        case   CDZImagePickerRecentAction:
            [self openRecentImage];
            break;
        case CDZImagePickerLibraryAction:
            [self openLibrary];
            break;
        case CDZImagePickerCloseAction:
            [self closeAction];
            break;
    }   
}

- (void)openCamera{
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){
        UIImagePickerController *pickerController = [[UIImagePickerController alloc]init];
        pickerController.delegate = self;
        pickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
        [self presentViewController:pickerController animated:NO completion:nil];
        NSLog(@"打開相機");
    }
}

- (void)openLibrary{
    UIImagePickerController *pickerController = [[UIImagePickerController alloc]init];
    pickerController.delegate = self;
    pickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    [self presentViewController:pickerController animated:NO completion:nil];
    NSLog(@"打開圖庫");
    
}

- (void)closeAction{
    [self dismissViewControllerAnimated:YES completion:nil];
    NSLog(@"關(guān)閉按鈕");
}


- (void)openRecentImage{
    [[PHImageManager defaultManager]requestImageForAsset:self.photosDataSource.itemArray[0] targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFill options:nil resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
        self.resultImage = result;
        [self dismissViewControllerAnimated:YES completion:nil];
        NSLog(@"打開最新圖片");
    }];
}

實現(xiàn)imagepicker的delegate(記得實現(xiàn)UINavigationControllerDelegate亡脸,不可只實現(xiàn)UIImagePickerControllerDelegate,會報錯

#pragma mark - imagePickerController delegate

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(nonnull NSDictionary<NSString *,id> *)info{
    UIImage *image = info[UIImagePickerControllerOriginalImage];
    if(picker.sourceType == UIImagePickerControllerSourceTypeCamera){
        UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
    }
    self.resultImage = image;
    [picker dismissViewControllerAnimated:NO completion:nil];
    [self dismissViewControllerAnimated:YES completion:nil];
    NSLog(@"從相機或圖庫獲取圖片");
}

iOS10還要檢查一下相機和圖庫的權(quán)限树酪,記得在info.plist中添加兩行浅碾,不然會崩潰

最后在是tableview初始化與繪制,在datasource初始化自己需要的按鈕item數(shù)組(acctionArray)

- (CDZImagePickerActionsDataSource *)actionsDataSource{
    if (!_actionsDataSource) {
        _actionsDataSource = [[CDZImagePickerActionsDataSource alloc]init];
        _actionsDataSource.itemArray = self.actionArray;
    }
    return _actionsDataSource;
}
- (UITableView *)actionView{
    if (!_actionView) {
        CGFloat actionsViewHeight = actionsViewCellHeight * self.actionArray.count;
        _actionView = [[UITableView alloc]initWithFrame:CGRectMake(0,SCREEN_HEIGHT - actionsViewHeight ,SCREEN_WIDTH, actionsViewHeight) style:UITableViewStylePlain];
        _actionView.scrollEnabled = NO; //不需要滑動
        _actionView.separatorStyle = UITableViewCellSeparatorStyleNone; //分割線去除
        _actionView.delegate = self;
        _actionView.dataSource = self.actionsDataSource;
    }
    return _actionView;
}


2.展示照片的collecionview

collecionview的實現(xiàn)思路和tableview類似

  • 自定義collectionview的cell實現(xiàn)樣式調(diào)整
  • 自定義Datasource解析并設置當前的cell對應的圖片
  • 用Photokit的方法抓取圖庫的照片
  • 實現(xiàn)collectionviewflowlayout的delegate實時調(diào)整cell的大小嗅回,邊距
  • 實現(xiàn)collecitonview的delegate及穗,設置點擊事件
  • 實現(xiàn)collectionview的初始化和視圖繪制

cell的自定義主要是重寫其initWithFrame方法添加imageview(注意collectionviewcell只用initWithFrame方式初始化,tableviewcell初始化方法有多種)绵载,并在layoutSubviews里改變imageview的大小,和tableview類似寫一個setCellFromItem的方法解析item數(shù)據(jù)埂陆。而Photokit獲取的圖片對象為phasset,用PHImageMange的方法根據(jù)將其解析為size為cell的大小uimage娃豹,加快加載速度焚虱,按需加載(關(guān)于Photokit的優(yōu)點不再贅述,iOS8以后蘋果只保留了Photokit用于獲取系統(tǒng)圖片)

photokit獲取時懂版,注意兩個地方鹃栽,targetSize傳入的是pixel,而不是平時用的size躯畴,且option里的resizeMode默認為None(不縮放)民鼓,不重寫resizeMode屬性那么只會抓取不縮放的圖,在意質(zhì)量可以設置resizeMode為Exact(精確)蓬抄,追求速度可以設置為Fast(這樣不會完全按照設置的targetSize去獲取圖片)

- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        [self.contentView addSubview:self.photoImageView];
    }
    return self;
}

- (void)layoutSubviews{
    [super layoutSubviews];
    self.photoImageView.frame = self.contentView.bounds;
}

- (void)setCellFromItem:(PHAsset *)asset{
  PHImageRequestOptions *options = [[PHImageRequestOptions alloc]init];
    options.resizeMode = PHImageRequestOptionsResizeModeFast;
    [[PHImageManager defaultManager]requestImageForAsset:asset targetSize:[UIScreen mainScreen].bounds.size contentMode:PHImageContentModeAspectFill options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
        self.photoImageView.image = result;
    }];
}

- (UIImageView *)photoImageView{
    if (!_photoImageView) {
        _photoImageView = [[UIImageView alloc]init];
        _photoImageView.contentMode = UIViewContentModeScaleAspectFill;
    }
    return _photoImageView;
}

datasource也類似丰嘉,定義一個itemArray用于存放圖片對象phasset
@property (nonatomic, strong) NSMutableArray *itemArray;
實現(xiàn)datasource方法,和tableview類似(不用判斷cell是nil是因為collectionviewcell的初始化只有一種

#pragma mark - collectionViewDataSourceRequried

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return self.itemArray.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    PHAsset *item = self.itemArray[indexPath.row];
    CDZImagePickerPhotosCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([CDZImagePickerPhotosCell class]) forIndexPath:indexPath];
    [cell setCellFromItem:item];
    return cell;
}

在controller中獲取全部照片
再用photokit的方法把所有圖片獲取到數(shù)組里

- (NSMutableArray *)getImageAssets{
    self.imageAssetsResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil];
    NSMutableArray *assets = [NSMutableArray new];
    for (PHAsset *asset in self.imageAssetsResult){
        [assets insertObject:asset atIndex:0];
    }
    return assets;
}

collectionview的布局嚷缭,cell的大小排列和間距等饮亏,都是由collectionviewlayout決定的,布局是線性的話阅爽,推薦用官方的子類collectionviewflowlayout路幸,其deleagate是collectionviewdeleagateflowlayout,是collectionviewdelegate的子delegate付翁。

- (UICollectionViewFlowLayout *)photosFlowLayout{
    if (!_photosFlowLayout) {
        _photosFlowLayout = [UICollectionViewFlowLayout new];
        _photosFlowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; //水平滾動
    }
    return _photosFlowLayout;
}
#pragma mark - collectionViewDelegateFlowLayout
//根據(jù)asset的長寬設定每個cell的size
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
    PHAsset *asset = self.photosDataSource.itemArray[indexPath.row];
    CGFloat height = photosViewHeight - 2 * photosViewInset;
    CGFloat aspectRatio = asset.pixelWidth / (CGFloat)asset.pixelHeight;
    CGFloat width = height * aspectRatio;
    CGSize size = CGSizeMake(width, height);
    return size;
}

//設置整個collectionview上下左右間距
- (UIEdgeInsets) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
    return UIEdgeInsetsMake(photosViewInset, photosViewInSet, photosViewInset, photosViewInset);
}

//設置cell之間的間距
- (CGFloat) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section{
    return photosViewInset;
}

設置collecionview的delegate和點擊事件

#pragma mark - collectionViewDelegate

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    [[PHImageManager defaultManager]requestImageForAsset:self.photosArray[indexPath.row] targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFill options:nil resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
        self.resultImage = result;
        [self dismissViewControllerAnimated:YES completion:nil];
        NSLog(@"已選擇圖片");
    }];
}

最后就是collectionview的初始化和繪制和tableview類似简肴,多了一步注冊collectionview的cell(collectionviewcell必須讓collecionview注冊重用標識,而tableviewcell可以在自己init方法里注冊

- (UICollectionView *)photosView{
    if (!_photosView){
        CGFloat actionsViewHeight = actionsViewCellHeight * self.actionArray.count;
        _photosView = [[UICollectionView alloc]initWithFrame:CGRectMake(0, SCREEN_HEIGHT - actionsViewHeight - photosViewHeight , SCREEN_WIDTH, photosViewHeight) collectionViewLayout:self.photosFlowLayout];
        _photosView.delegate = self;
        _photosView.dataSource = self.photosDataSource;
        _photosView.backgroundColor = [UIColor whiteColor];
        _photosView.showsHorizontalScrollIndicator = NO;
        [_photosView registerClass:[CDZImagePickerPhotosCell class] forCellWithReuseIdentifier:NSStringFromClass([CDZImagePickerPhotosCell class])];
    }
    return _photosView;
}
- (CDZImagePickerPhotosDataSource *)photosDataSource{
    if (!_photosDataSource){
        _photosDataSource = [[CDZImagePickerPhotosDataSource alloc]init];
        _photosDataSource.itemArray = [self getImageAssets];
    }
    return _photosDataSource;
}

3.透明圖層(用于實現(xiàn)空白處點擊消失)

比較簡單百侧,增加一個tap手勢即可砰识,直接上代碼(記得實現(xiàn)UIGestureRecognizerDelegate)

- (UIView *)backgroundView{
    if (!_backgroundView){
        CGFloat actionsViewHeight = actionsViewCellHeight * self.actionArray.count;
        _backgroundView =[[UIView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT - photosViewHeight - actionsViewHeight)];
        _backgroundView.backgroundColor.opaque = YES;//設置為透明
        _backgroundView.userInteractionEnabled = YES;
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(dissPicker:)];
        [_backgroundView addGestureRecognizer:tap];
    }
    return _backgroundView;
}

4.收尾&封裝

定義一個block用于回調(diào)傳照片
typedef void (^CDZImageResultBlock) (UIImage *image);
一個內(nèi)部block屬性
@property (nonatomic ,copy) CDZImageResultBlock block;
封裝方法

- (void)openPickerInController:(UIViewController *)controller withImageBlock:(CDZImageResultBlock)imageBlock{
    self.modalPresentationStyle = UIModalPresentationOverCurrentContext;//iOS8上默認presentviewcontroller不透明杂伟,需設置style
    self.block = imageBlock;
    [controller presentViewController:self animated:YES completion:nil];
}

在Dealloc方法里調(diào)用block回調(diào)

- (void)dealloc{
    self.block(self.resultImage);
    NSLog(@"ImagePicker已銷毀");
}

5.使用方法

將CDZImagePicker文件夾拖入項目

CDZImagePickerViewController *imagePickerController = [[CDZImagePickerViewController alloc]init];
[imagePickerController openPickerInController:self withImageBlock:^(UIImage *image) {
        if (image) { //如果沒選照片會回調(diào)nil,若想讓之前照片不變就加上判斷
           //yourcode
        }
       // yourcode
}];

也可以自定義CDZActionsItem自定義文字和圖片

imagePickerController.actionArray = [NSMutableArray arrayWithObjects:
                      [[CDZImagePickerActionsItem alloc]initWithTitle:@"打開設備上的圖片" withActionType:CDZImagePickerLibraryAction withImage:[UIImage imageNamed:@"phone-icon.png"]],
                      [[CDZImagePickerActionsItem alloc]initWithTitle:@"相機" withActionType:CDZImagePickerCameraAction withImage:[UIImage imageNamed:@"camera-icon.png"]],
                      [[CDZImagePickerActionsItem alloc]initWithTitle:@"打開最新圖片" withActionType:CDZImagePickerRecentAction withImage:[UIImage imageNamed:@"clock-icon.png"]],
                      nil];

效果就和snapseed很像啦仍翰!


最后

所有源碼和Demo
雖然不是什么很難的輪子,但希望能分享給有需要的人观话。

關(guān)于后續(xù)的處理照片變更和獲取相冊權(quán)限另寫了一篇簡書文章予借,歡迎閱讀。

如果您覺得有幫助,不妨給個star鼓勵一下,歡迎關(guān)注&交流
有任何問題歡迎評論私信或者提issue
QQ:757765420
Email:nemocdz@gmail.com
Github:Nemocdz
微博:@Nemocdz

謝謝觀看

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末频蛔,一起剝皮案震驚了整個濱河市灵迫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌晦溪,老刑警劉巖瀑粥,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異三圆,居然都是意外死亡狞换,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門舟肉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來修噪,“玉大人,你說我怎么就攤上這事路媚』魄恚” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵整慎,是天一觀的道長脏款。 經(jīng)常有香客問我,道長裤园,這世上最難降的妖魔是什么撤师? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮比然,結(jié)果婚禮上丈氓,老公的妹妹穿的比我還像新娘。我一直安慰自己强法,他們只是感情好万俗,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著饮怯,像睡著了一般闰歪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蓖墅,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天库倘,我揣著相機與錄音临扮,去河邊找鬼。 笑死教翩,一個胖子當著我的面吹牛杆勇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播饱亿,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼蚜退,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了彪笼?” 一聲冷哼從身側(cè)響起钻注,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎配猫,沒想到半個月后幅恋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡泵肄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年捆交,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腐巢。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡零渐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出系忙,到底是詐尸還是另有隱情诵盼,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布银还,位于F島的核電站风宁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蛹疯。R本人自食惡果不足惜戒财,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捺弦。 院中可真熱鬧饮寞,春花似錦、人聲如沸列吼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽寞钥。三九已至慌申,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間理郑,已是汗流浹背蹄溉。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工咨油, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人柒爵。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓役电,卻偏偏與公主長得像,于是被迫代替她去往敵國和親棉胀。 傳聞我的和親對象是個殘疾皇子宴霸,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容