iOS 開發(fā)小技巧---baseViewcontroller

還在為每個視圖控制器都要自定義導航欄按鈕而煩躁嗎雷恃?還在為每個視圖控制器的列表都要添加一遍上拉刷新下拉加載而焦心嗎?還在為每個列表添加空白提示而煩惱嗎抒巢?不要怕胎撇,你需要一個基類介粘,一個救你于水火的VC的基類!

當你看到一個VC中幾千行代碼的時候你是絕望的创坞,崩潰的碗短!那些每個界面都要寫的代碼就是冗余代碼,我們完全可以在vc的基類中來寫這些代碼题涨,只要vc繼承基類vc偎谁,那么我們只需要一句代碼就可以完成創(chuàng)建導航左右按鈕,為列表添加上拉上推事件等等繁瑣而相同的事情纲堵!既能優(yōu)化界面巡雨,又能讓你的代碼略微提升那么一點逼格,何樂而不為呢席函,是吧老鐵~ 好了铐望,廢話結(jié)束,進入正題茂附!

首先在返回按鈕跟設(shè)置的普通返回按鈕不同的時候正蛙,或者導航欄右按鈕需要有點擊事件的時候,一般我們會選擇自定義導航欄左右按鈕营曼。當一個APP有多個界面需要自定義左右按鈕的時候乒验,這些代碼充斥在多個視圖控制器中會顯得雜亂和冗余,這時候我們就可以在基類VC中這么寫蒂阱,.h文件

typedef void(^navigationBtnBlock)(UIButton *navBtn);

@interface BaseViewController : UIViewController
/** navigationBar item click */
@property (nonatomic, copy)navigationBtnBlock navBtnAction;
@end

.m文件

#pragma mark ------- set nav left item
-(void)createBackBtnWithImageName:(NSString *)imageName{
    UIButton *btn_back = [UIButton buttonWithType:UIButtonTypeCustom];
    btn_back.frame = CGRectMake(0, 0, 40, 30);
    btn_back.contentEdgeInsets = UIEdgeInsetsMake(0, -2, 0, 0);
    btn_back.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
    [btn_back setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
    btn_back.tag = 9;
    //保證所有touch事件button的highlighted屬性為NO,即可去除高亮效果
    [btn_back addTarget:self action:@selector(preventFlicker:) forControlEvents:UIControlEventAllTouchEvents];
    [btn_back addTarget:self action:@selector(popToLastVC:) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *leftItem = [[UIBarButtonItem alloc]initWithCustomView:btn_back];
    self.navigationItem.leftBarButtonItem = leftItem;
}
-(void)popToLastVC:(UIButton *)sender{
    NSArray *viewControllers = self.navigationController.viewControllers;
    if (viewControllers.count == 0) {
        return;
    }
    if (self.navBtnAction) {//如果vc中調(diào)用了block回調(diào)就走自己的回調(diào)
        self.navBtnAction(sender);
    }else{//如果vc中沒有調(diào)用block回調(diào)锻全,直接返回到到上級頁面
        [self.navigationController popViewControllerAnimated:YES];
    }
}
#pragma mark ------- wipe out button highlight
-(void)preventFlicker:(UIButton *)sender{
    sender.highlighted = NO;
}

#pragma mark ------- set nav right item
-(void)createNavRightBtnWithImageName:(NSString *)imageName{
    UIButton *btn_right = [UIButton buttonWithType:UIButtonTypeCustom];
    btn_right.frame = CGRectMake(0, 0, 40, 30);
    btn_right.contentEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 2);
    btn_right.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
    [btn_right setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
    btn_right.tag = 10;
    //保證所有touch事件button的highlighted屬性為NO,即可去除高亮效果
    [btn_right addTarget:self action:@selector(preventFlicker:) forControlEvents:UIControlEventAllTouchEvents];
    [btn_right addTarget:self action:@selector(rightBtnClick:) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *rightItem = [[UIBarButtonItem alloc]initWithCustomView:btn_right];
    self.navigationItem.rightBarButtonItem = rightItem;
}
-(void)rightBtnClick:(UIButton *)sender{
    NSArray *viewControllers = self.navigationController.viewControllers;
    if (viewControllers.count == 0) {
        return;
    }
    if (self.navBtnAction) {//如果vc中調(diào)用了block回調(diào)就走自己的回調(diào)
        self.navBtnAction(sender);
    }else{//如果vc中沒有調(diào)用block回調(diào)狂塘,直接返回到到上級頁面
        [self.navigationController popViewControllerAnimated:YES];
    }
}

在繼承基類的VC中我們可以這樣調(diào)用

[self createBackBtnWithImageName:@"圖片名字"];
    
//如果不設(shè)置這個block回到,點擊返回按鈕就會直接返回上一級界面
self.navBtnAction = ^(UIButton * _Nonnull navBtn) {
  //這里可以通過按鈕的tag值來判斷點擊的是按鈕
};

列表是大部分APP都會頻繁使用的控件鳄厌,但是為tableView設(shè)置上推和下拉的事件卻是比較繁瑣的荞胡,我們可以在基類中這樣來寫,.h文件代碼如下:

typedef void(^tableViewRefreshBlock)(NSInteger pageNum);
typedef void(^navigationBtnBlock)(UIButton *navBtn);

@interface BaseViewController : UIViewController
/** tableView pull or push action */
@property (nonatomic, copy)tableViewRefreshBlock tableViewRefresh;
/** add tableview pull and push action */
-(void)addTableHeaderRefresh:(BOOL)headerRefresh tableFooterRefresh:(BOOL)footerRefresh;
@end

.m文件代碼

#pragma mark ------- tableView pull and push action
-(void)addTableHeaderRefresh:(BOOL)headerRefresh tableFooterRefresh:(BOOL)footerRefresh{
    if (headerRefresh) {
        MJRefreshNormalHeader *header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(reloadData)];
        header.lastUpdatedTimeLabel.hidden = YES;
        [header setTitle:@"拼命加載中..." forState:MJRefreshStateRefreshing];
        [header setTitle:@"下拉刷新數(shù)據(jù)~" forState:MJRefreshStateIdle];
        self.tableView.mj_header = header;
    }
    
    if (footerRefresh) {
        MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)];
        [footer setTitle:@"我是有底線的~" forState:MJRefreshStateNoMoreData];
        [footer setTitle:@"拼命加載中..." forState:MJRefreshStateRefreshing];
        [footer setTitle:@"上拉更多精彩~" forState:MJRefreshStateIdle];
        footer.stateLabel.textColor = [UIColor lightGrayColor];
        self.tableView.mj_footer = footer;
    }
}

-(void)reloadData{
    _pageNum = 1;
    if (self.tableViewRefresh) {
        self.tableViewRefresh(_pageNum);
    }
}

-(void)loadMoreData{
    _pageNum ++;
    if (self.tableViewRefresh) {
        self.tableViewRefresh(_pageNum);
    }
}

-(void)tableViewEndRefresh{
    [self.tableView.mj_header endRefreshing];
    [self.tableView.mj_footer endRefreshing];
}

在繼承基類的VC中我們可以這樣調(diào)用

    //設(shè)置為YES就添加事件了嚎,為NO則不添加事件
    [self addTableHeaderRefresh:YES tableFooterRefresh:YES];

    self.tableViewRefresh = ^(NSInteger pageNum) {
      //回調(diào)回來當前的pageNum
      //這里可以進行數(shù)據(jù)請求
    };

還有一些其他的方法泪漂,提示框,空白頁等等諸如此類都已在基類中設(shè)置新思,我總結(jié)了一下分享給大家,一起學習~

typedef void(^navigationBtnBlock)(UIButton *navBtn);
typedef void(^tableViewRefreshBlock)(NSInteger pageNum);

@interface BaseViewController : UIViewController

/** navigationBar item click */
@property (nonatomic, copy)navigationBtnBlock navBtnAction;
/** tableView pull or push action */
@property (nonatomic, copy)tableViewRefreshBlock tableViewRefresh;
/** tableView */
@property (nonatomic, strong)UITableView *tableView;
/** tableView current page */
@property (nonatomic, assign)NSInteger pageNum;

/** set custom navigation leftItem */
-(void)createBackBtnWithImageName:(NSString *)imageName;

/** set navigationbar backgroundColor */
-(void)setNavigationBarBackgroundImageWithColor:(UIColor *)color;

/** set custom navigation rightItem */
-(void)createNavRightBtnWithImageName:(NSString *)imageName;

/** show custom aleartView */
-(void)showMessageBottomWithText:(NSString *)text;

/** add tableview pull and push action */
-(void)addTableHeaderRefresh:(BOOL)headerRefresh tableFooterRefresh:(BOOL)footerRefresh;

/** show custom hud */
-(void)showCustomMessageViewWithText:(NSString *)text;
/** hide custom hud */
-(void)hiddenCustomMessageView;

/** show custom activeIndicator*/
-(void)showCustomActiveIndicator;
/**hide custom activeIndicator*/
-(void)hiddenCustomActiveIndicator;
@end

.m文件

@interface BaseViewController (){
    UIImageView *navBarHairlineImageView;
    UIActivityIndicatorView *_activityIndicator;
}

@end

@implementation BaseViewController

-(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    navBarHairlineImageView.hidden = YES;
}


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    //prevent view move down
    self.automaticallyAdjustsScrollViewInsets = NO;
    //set view backgroundColor
    self.view.backgroundColor = [UIColor whiteColor];
    //set nav backgroundColor
    [self setNavigationBarBackgroundImageWithColor:[UIColor whiteColor]];
    //wipe out the black line under the navigation bar
    UINavigationBar *navigationBar = self.navigationController.navigationBar;
    navBarHairlineImageView = [self  slnFindHairlineImageViewUnder:navigationBar];
}

#pragma mark ------- set nav left item
-(void)createBackBtnWithImageName:(NSString *)imageName{
    UIButton *btn_back = [UIButton buttonWithType:UIButtonTypeCustom];
    btn_back.frame = CGRectMake(0, 0, 40, 30);
    btn_back.contentEdgeInsets = UIEdgeInsetsMake(0, -2, 0, 0);
    btn_back.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
    [btn_back setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
    btn_back.tag = 9;
    //保證所有touch事件button的highlighted屬性為NO,即可去除高亮效果
    [btn_back addTarget:self action:@selector(preventFlicker:) forControlEvents:UIControlEventAllTouchEvents];
    [btn_back addTarget:self action:@selector(popToLastVC:) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *leftItem = [[UIBarButtonItem alloc]initWithCustomView:btn_back];
    self.navigationItem.leftBarButtonItem = leftItem;
}
-(void)popToLastVC:(UIButton *)sender{
    NSArray *viewControllers = self.navigationController.viewControllers;
    if (viewControllers.count == 0) {
        return;
    }
    if (self.navBtnAction) {//如果vc中調(diào)用了block回調(diào)就走自己的回調(diào)
        self.navBtnAction(sender);
    }else{//如果vc中沒有調(diào)用block回調(diào)窖梁,直接返回到到上級頁面
        [self.navigationController popViewControllerAnimated:YES];
    }
}
#pragma mark ------- wipe out button highlight
-(void)preventFlicker:(UIButton *)sender{
    sender.highlighted = NO;
}

#pragma mark ------- set nav right item
-(void)createNavRightBtnWithImageName:(NSString *)imageName{
    UIButton *btn_right = [UIButton buttonWithType:UIButtonTypeCustom];
    btn_right.frame = CGRectMake(0, 0, 40, 30);
    btn_right.contentEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 2);
    btn_right.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
    [btn_right setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
    btn_right.tag = 10;
    //保證所有touch事件button的highlighted屬性為NO,即可去除高亮效果
    [btn_right addTarget:self action:@selector(preventFlicker:) forControlEvents:UIControlEventAllTouchEvents];
    [btn_right addTarget:self action:@selector(rightBtnClick:) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *rightItem = [[UIBarButtonItem alloc]initWithCustomView:btn_right];
    self.navigationItem.rightBarButtonItem = rightItem;
}
-(void)rightBtnClick:(UIButton *)sender{
    NSArray *viewControllers = self.navigationController.viewControllers;
    if (viewControllers.count == 0) {
        return;
    }
    if (self.navBtnAction) {//如果vc中調(diào)用了block回調(diào)就走自己的回調(diào)
        self.navBtnAction(sender);
    }else{//如果vc中沒有調(diào)用block回調(diào)赘风,直接返回到到上級頁面
        [self.navigationController popViewControllerAnimated:YES];
    }
}
#pragma mark ------- create a image
-(UIImage *)createImageWithView:(UIView *)view{
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, 0, [[UIScreen mainScreen] scale]);
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return viewImage;
}
-(UIView *)getColorViewWithColor:(UIColor *)color{
    UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, navgationBarH)];
    view.backgroundColor = color;
    if (CGColorEqualToColor(color.CGColor, [UIColor clearColor].CGColor) || CGColorEqualToColor(color.CGColor, APPColor.CGColor)) {
        
    }else{
        view.layer.borderColor = ColorWithRGB(239, 239, 239).CGColor;
        view.layer.borderWidth = 0.5;
        view.layer.shadowColor=[ColorWithRGB(200, 200, 200) colorWithAlphaComponent:0.8].CGColor;
        view.layer.shadowOffset=CGSizeMake(-4,-4);
        view.layer.shadowOpacity=0.5;
        view.layer.shadowRadius=4;
        view.clipsToBounds = false;
    }
    return view;
}
#pragma mark ------- get nav bottom black line
-(UIImageView *)slnFindHairlineImageViewUnder:(UIView *)view {
    if ([view isKindOfClass:UIImageView.class] && view.bounds.size.height <= 1.0){
        return (UIImageView *)view;
    }
    for (UIView *subview in view.subviews) {
        UIImageView *imageView = [self slnFindHairlineImageViewUnder:subview];
        if (imageView) {
            return imageView;
        }
    }
    return nil;
}

#pragma mark ------- set nav background color
-(void)setNavigationBarBackgroundImageWithColor:(UIColor *)color{
    [self.navigationController.navigationBar setBackgroundImage:[self createImageWithView:[self getColorViewWithColor:color]] forBarMetrics:UIBarMetricsDefault];
}

#pragma mark ------- show messgae on view of bottom
-(void)showMessageBottomWithText:(NSString *)text{
    [CLToast showBottomWithText:NSLocalizedString(text, nil)];
}

#pragma mark ------- tableView pull and push action
-(void)addTableHeaderRefresh:(BOOL)headerRefresh tableFooterRefresh:(BOOL)footerRefresh{
    if (headerRefresh) {
        MJRefreshNormalHeader *header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(reloadData)];
        header.lastUpdatedTimeLabel.hidden = YES;
        [header setTitle:@"拼命加載中..." forState:MJRefreshStateRefreshing];
        [header setTitle:@"下拉刷新數(shù)據(jù)~" forState:MJRefreshStateIdle];
        self.tableView.mj_header = header;
    }
    
    if (footerRefresh) {
        MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)];
        [footer setTitle:@"我是有底線的~" forState:MJRefreshStateNoMoreData];
        [footer setTitle:@"拼命加載中..." forState:MJRefreshStateRefreshing];
        [footer setTitle:@"上拉更多精彩~" forState:MJRefreshStateIdle];
        footer.stateLabel.textColor = [UIColor lightGrayColor];
        self.tableView.mj_footer = footer;
    }
}

-(void)reloadData{
    _pageNum = 1;
    if (self.tableViewRefresh) {
        self.tableViewRefresh(_pageNum);
    }
}

-(void)loadMoreData{
    _pageNum ++;
    if (self.tableViewRefresh) {
        self.tableViewRefresh(_pageNum);
    }
}

-(void)tableViewEndRefresh{
    [self.tableView.mj_header endRefreshing];
    [self.tableView.mj_footer endRefreshing];
}

#pragma mark ------- custom hud
-(void)showCustomMessageViewWithText:(NSString *)text{
    MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    //修改樣式夹囚,否則等待框背景色將為半透明
    hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
    //設(shè)置等待框背景色為黑色
    hud.bezelView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.8];
    //設(shè)置菊花框為白色
    [UIActivityIndicatorView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]].color = [UIColor whiteColor];
    hud.label.text = NSLocalizedString(text, nil);
    hud.label.textColor = [UIColor whiteColor];
    hud.userInteractionEnabled = YES;
    hud.mode = MBProgressHUDModeIndeterminate;
}
-(void)hiddenCustomMessageView{
    [MBProgressHUD hideHUDForView:self.view animated:YES];
}

#pragma mark ------- custom activeIndicator
-(void)showCustomActiveIndicator{
   _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    [self.view addSubview:_activityIndicator];
    _activityIndicator.frame = CGRectMake((SCREEN_WIDTH - 99*WidthRatio)/2, (SCREEN_HEIGHT-99*WidthRatio)/2 - (navgationBarH + tabbarH)/2, 99*WidthRatio, 99*WidthRatio);
    _activityIndicator.hidesWhenStopped = NO;
    [_activityIndicator startAnimating];
}
-(void)hiddenCustomActiveIndicator{
    [_activityIndicator stopAnimating];
    [_activityIndicator removeFromSuperview];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    [[SDImageCache sharedImageCache] clearMemory];//清內(nèi)存
    [[SDImageCache sharedImageCache] clearDiskOnCompletion:nil];//清內(nèi)存
    // 1.取消正在下載的操作
    [[SDWebImageManager sharedManager] cancelAll];
    // 2.清除內(nèi)存緩存
    [[SDWebImageManager sharedManager].imageCache clearMemory];
}

多多指教,共同進步~ 點個贊再走唄~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末邀窃,一起剝皮案震驚了整個濱河市荸哟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瞬捕,老刑警劉巖鞍历,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異肪虎,居然都是意外死亡劣砍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門扇救,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刑枝,“玉大人,你說我怎么就攤上這事迅腔∽俺” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵沧烈,是天一觀的道長掠兄。 經(jīng)常有香客問我,道長锌雀,這世上最難降的妖魔是什么蚂夕? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮腋逆,結(jié)果婚禮上婿牍,老公的妹妹穿的比我還像新娘。我一直安慰自己闲礼,他們只是感情好牍汹,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布铐维。 她就那樣靜靜地躺著,像睡著了一般慎菲。 火紅的嫁衣襯著肌膚如雪嫁蛇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天露该,我揣著相機與錄音睬棚,去河邊找鬼。 笑死解幼,一個胖子當著我的面吹牛抑党,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播撵摆,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼底靠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了特铝?” 一聲冷哼從身側(cè)響起暑中,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鲫剿,沒想到半個月后鳄逾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡灵莲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年雕凹,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片政冻。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡枚抵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赠幕,到底是詐尸還是另有隱情俄精,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布榕堰,位于F島的核電站竖慧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏逆屡。R本人自食惡果不足惜圾旨,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望魏蔗。 院中可真熱鬧砍的,春花似錦、人聲如沸莺治。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至床佳,卻和暖如春滋早,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背砌们。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工杆麸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人浪感。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓昔头,卻偏偏與公主長得像,于是被迫代替她去往敵國和親影兽。 傳聞我的和親對象是個殘疾皇子揭斧,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354