一個java程序員自學IOS開發之路(五)


2015/10/24

Day 23

我插入移動硬盤后,電腦右上角老是出現一個齒輪轉啊轉,然后彈出對話框說有新文件加入電腦什么文件夾,要不要去看,關還關不掉,於是乎,昨晚腦袋一抽就把那個彈出來的文件夾移入廢紙簍,然后發現不能這么干,就從廢紙簍恢復,然而Finder就卡死了= =不管怎樣都沒響應,我煩的不行就強制按電源鍵重啟電腦了(用Win系統的壞習慣,有事沒事重啟一下),然后!!!輸入密碼登陸,如下圖:

 

然后就重啟了= =,無限循環進不了系統,各種方法無果后,沒辦法去了售后店。解決方法,重裝系統,花費300大洋,我去!真特么貴,唉~誰叫自己作呢,弄好后回家第一件事就是自制U盤啟動盤。

其實很簡單,先下好鏡像文件,然后插入U盤,格式化它,再可以給它分區,這個隨意我就沒分,最后終端輸入命令,等着就行了。

命令如下

安裝包名稱:Install OS X El Capitan.app

制作U盤啟動盤指令

sudo /Applications/Install\ OS\ X\ El\ Capitan.app/Contents/Resources/createinstallmedia --volume /Volumes/u盤 --applicationpath /Applications/Install\ OS\ X\ El\ Capitan.app --nointeraction

第二件事就是開啟TimeMachine備份。

之前寫的代碼都沒了= =,唉,不說這事了,都是淚%>_<%

 

2015/10/25

Day 24

調試新系統,調整心情,再一次安裝Xcode,繼續學習啦~

今天開始學習UITableView

UITableView

在眾多app中,能看到各式各樣的表格數據,如下

 

iOS中,要實現表格數據展示,最常用的做法就是使用UITableView

UITableView繼承自UIScrollView,因此支持垂直滾動,而且性能極佳

UITableView的兩種樣式

  1. UITableView需要一個數據源(dataSource)來顯示數據
  2. UITableView會向數據源查詢一共有多少行數據以及每一行顯示什么數據等
  3. 沒有設置數據源的UITableView只是個空殼
  4. 凡是遵守UITableViewDataSource協議的OC對象,都可以是UITableView的數據源

UITableViewDataSource協議

 

Cell簡介

UITableView的每一行都是一個UITableViewCell,通過dataSource的tableView:cellForRowAtIndexPath:方法來初始化每一行

UITableViewCell內部有個默認的子視圖:contentView,contentView是UITableViewCell所顯示內容的父視圖,可顯示一些輔助指示視圖。還可以通過cell的accessoryView屬性來自定義輔助指示視圖(比如往右邊放一個開關)

UITableViewCellcontentView

contentView下默認有3個子視圖

其中2個是UILabel(通過UITableViewCell的textLabel和detailTextLabel屬性訪問)

第3個是UIImageView(通過UITableViewCell的imageView屬性訪問)

UITableViewCell還有一個UITableViewCellStyle屬性,用於決定使用contentView的哪些子視圖,以及這些子視圖在contentView中的位置

 

Cell的重用

  • iOS設備的內存有限,如果用UITableView顯示成千上萬條數據,就需要成千上萬個UITableViewCell對象的話,那將會耗盡iOS設備的內存。要解決該問題,需要重用UITableViewCell對象
  • 重用原理:當滾動列表時,部分UITableViewCell會移出窗口,UITableView會將窗口外的UITableViewCell放入一個對象池中,等待重用。當UITableView要求dataSource返回UITableViewCell時,dataSource會先查看這個對象池,如果池中有未使用的UITableViewCell,dataSource會用新的數據配置這個UITableViewCell,然后返回給UITableView,重新顯示到窗口中,從而避免創建新對象
  • 還有一個非常重要的問題:有時候需要自定義UITableViewCell(用一個子類繼承UITableViewCell),而且每一行用的不一定是同一種UITableViewCell,所以一個UITableView可能擁有不同類型的UITableViewCell,對象池中也會有很多不同類型的UITableViewCell,那么UITableView在重用UITableViewCell時可能會得到錯誤類型的UITableViewCell
  • 解決方案:UITableViewCell有個NSString *reuseIdentifier屬性,可以在初始化UITableViewCell的時候傳入一個特定的字符串標識來設置reuseIdentifier(一般用UITableViewCell的類名)。當UITableView要求dataSource返回UITableViewCell時,先通過一個字符串標識到對象池中查找對應類型的UITableViewCell對象,如果有,就重用,如果沒有,就傳入這個字符串標識來初始化一個UITableViewCell對象

Cell的重用代碼

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    // 1.定義一個cell的標識

      static NSString *yu3 = @“yu3”;

    // 2.從緩存池中取出cell

      UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:yu3];

    // 3.如果緩存池中沒有cell

      if (cell == nil) {

        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:yu3];

    }

    // 4.設置cell的屬性...

    

      return cell;

}

上面的寫法並不好,因為數據源不需要知道cell內部的標識為什么,cell的標識cell自己最清楚,應該把創建cell的代碼封裝在cell里面,如下(YUCell為我自定義的cell類名)

+ (instancetype)cellWithTableView:(UITableView *)tableView{

    static NSString *yu3 = @“yu3Cell";

    YUCell *cell = [tableView dequeueReusableCellWithIdentifier:yu3];

    if (cell == nil) {

        cell = [[YUCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:yu3];

    }

    return cell;

}

這樣,數據源里的方法就很簡單

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    //創建cell

    YUCell *cell = [YUCell cellWithTableView:tableView];

    //設置cell的屬性...

    

    return cell;

}

這樣的話,如果以后cell里面的東西要變化,只需要動cell里的代碼,數據源里代碼不需要改變。

2015/10/26

Day 25

今天做了一個UIScrollView和UITableView綜合的小項目

    

仿造團購app的界面,上面的廣告可以自動翻頁,滑到下面有按鈕點擊后加載數據

 

上面的廣告,每個cell還有下面的加載更多按鈕都是用xib封裝的

 

使用xib封裝一個view的步驟:

1.新建一個xib文件描述一個view的內部結構

2.新建一個自定義的類(自定義類需要繼承自系統自帶的view, 繼承自哪個類,  取決於xib根對象的Class)

3.新建類的類名最好跟xib的文件名保持一致

4.將xib中的控件 自定義類的.m文件 進行連線

5.提供一個類方法返回一個創建好的自定義view(屏蔽從xib加載的過程)

6.提供一個模型屬性讓外界傳遞模型數據

7.重寫模型屬性的setter方法,在這里將模型數據展示到對應的子控件上面

代理的使用

像上面的加載更多按鈕,他是在xib里面的,如果想讓用戶點擊它加載新數據,這件事得控制器ViewController來做。可以用代理模式實現,讓ViewController成為YUTgFooterView(我封裝該View的類名)的代理(delegate),這樣ViewController就可以監聽那個按鈕點擊做出反應了。

使用delegate的步驟

  • 先搞清楚誰是誰的代理(delegate)
  • 定義代理協議,協議名稱的命名規范:控件類名 + Delegate
  • 定義代理方法
    • 代理方法一般都定義為@optional
    • 代理方法名都以控件名開頭
    • 代理方法至少有1個參數,將控件本身傳遞出去

 

  • 設置代理(delegate)對象  (比如myView.delegate = xxxx;)

 

  • 代理對象遵守協議

 

  • 代理對象實現協議里面該實現的方法

 

  • 在恰當的時刻調用代理對象(delegate)的代理方法,通知代理發生了什么事情(在調用之前判斷代理是否實現了該代理方法)

 

 

2015/10/27

Day 26

昨天的項目中,cell是xib封裝的,每個cell的內容都是類似的,高度也是一樣的,這樣太局限,比如微博頁面,它的每個cell的內容是不一樣的,這就需要用代碼來自定義cell 

如圖,每個cell最多可以有一個頭像,一個名字,一個vip標識,一個文本,一張圖片,plist文件結構如下 

   

首先,把plist封裝成模型

 

創建自定義的cell類繼承自UItableViewCell 

 

cell內部的控件屬性要寫在.m文件中的類擴展中 

 

實現cell的構造方法

/**

 *  構造方法(在初始化對象的時候會調用)

 *  一般在這個方法中添加需要顯示的子控件

 */

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {

    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {

        UIImageView *iconView = [[UIImageView alloc] init];

        [self.contentView addSubview:iconView];

        self.iconView = iconView;

        UILabel *nameView = [[UILabel alloc] init];

        nameView.font = YUNameFont;

        nameView.backgroundColor = [UIColor orangeColor];

        [self.contentView addSubview:nameView];

        self.nameView = nameView;

        UIImageView *vipView = [[UIImageView alloc] init];

        [self.contentView addSubview:vipView];

        self.vipView = vipView;

        UILabel *textView = [[UILabel alloc] init];

        textView.font = YUTextFont;

        textView.numberOfLines = 0;//自動換行!

        textView.backgroundColor = [UIColor blueColor];

        [self.contentView addSubview:textView];

        self.textView = textView;

        UIImageView *pictureView = [[UIImageView alloc] init];

        [self.contentView addSubview:pictureView];

        self.pictureView = pictureView;

    }

    return self;

}

再重寫成員屬性的setter方法,里面設置控件數據

/**設置子控件的大小和數據 */

-(void)setStatus:(YUStatus *)status {

    _status = status;

    //設置數據

    [self setData];

    //設置控件大小

    [self setFrame];

}

設置的代碼冗長,我把他們封裝了

別忘了實現.h中聲明的類方法

+ (instancetype)cellWithTableView:(UITableView *)tableView{

    static NSString *yu3 = @"statusCell";

    YUStatusCell *cell = [tableView dequeueReusableCellWithIdentifier:yu3];

    if (cell == nil) {

        cell = [[YUStatusCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:yu3];

    }

    return cell;

}

最后到控制器,先讓控制器繼承自UITableViewController 

  

設置成員屬性 

 

重寫getter方法加載數據

- (NSArray *)statuses {

    NSMutableArray *result = [[NSMutableArray alloc] init];

    NSString *path = [[NSBundle mainBundle] pathForResource:@"statuses.plist" ofType:nil];

    NSArray *dics = [NSArray arrayWithContentsOfFile:path];

    for (NSDictionary *dic in dics) {

        YUStatus *status = [YUStatus statusWithDic:dic];

        [result addObject:status];

    }

    return result;

}

最后重寫數據源方法

//默認就是1,可以不寫這個方法

//- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

//    return 1;

//}

 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    return self.statuses.count;

}

 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    YUStatusCell *cell = [YUStatusCell cellWithTableView:tableView];

    cell.status = self.statuses[indexPath.row];

    return cell;

}

這樣運行出來是這個效果,很坑爹,因為個cell默認的高度是一樣的,有個方法可以改變 

 

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    //如何自定義cell高度?

    return 100;

}

這里插播一下,計算一個字符串所占的size,如圖我文本的背景色

第一種方法,利用控件的一個方法

//根據內部文字以及參數中給的參考size計算自適應的size

    CGSize nameSize = [self.nameView sizeThatFits:CGSizeZero];

第二種方法,利用NSString一個類別(分類)中的方法 

//根據內部文字以及參數中給的參考的最大size計算自適應的size

    CGSize nameSize = [status.name boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:14]} context:nil].size;

第一個參數:參考的最大size,這里可以規定最大寬度和高度

第二個參數:一般寫NSStringDrawingUsesLineFragmentOrigin |NSStringDrawingUsesFontLeading

第三個參數:是個字典,傳入字體大小

 

那么問題來了,如何計算每個cell的高度?

cell的高度是根據里面的數據決定的,數據是由模型傳過來的,因此cell的高度應該由模型來計算!

解決方法:

1.提供2個模型

  • 數據模型: 存放文字數據\圖片數據
  • frame模型: 存放數據模型\所有子控件的frame\cell的高度 

 

2.cell擁有一個frame模型(frame模型已經包含數據模型了)

 

3.重寫frame模型屬性的setter方法: 在這個方法中設置子控件的顯示數據和frame

- (void)setStatus:(YUStatus *)status {

    _status = status;

    CGFloat padding = 20;

    CGFloat iconX = padding;

    CGFloat iconY = padding;

    CGFloat iconW = 30;

    CGFloat iconH = 30;

    _iconF = CGRectMake(iconX, iconY, iconW, iconH);

    //根據內部文字以及參數中給的參考的最大size計算自適應的size

    CGSize nameSize = [status.name boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : YUNameFont} context:nil].size;

    CGFloat nameX = CGRectGetMaxX(_iconF) + padding;

    CGFloat nameY = iconY + (iconH - nameSize.height) / 2;

    _nameF = CGRectMake(nameX, nameY, nameSize.width, nameSize.height);

    

    CGFloat vipX = nameX + nameSize.width + padding;

    CGFloat vipY = nameY;

    CGFloat vipW = 14;

    CGFloat vipH = 14;

    _vipF = CGRectMake(vipX, vipY, vipW, vipH);

    

    CGFloat textX = iconX;

    CGFloat textY = CGRectGetMaxY(_iconF) + padding;

    CGSize textSize = [status.text boundingRectWithSize:CGSizeMake(345, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName : YUTextFont} context:nil].size;

    _textF = CGRectMake(textX, textY, textSize.width, textSize.height);

    

    if (status.picture) {

        CGFloat picX = textX;

        CGFloat picY = CGRectGetMaxY(_textF) + padding;

        CGFloat picW = 200;

        CGFloat picH = 200;

        _picF = CGRectMake(picX, picY, picW, picH);

        _cellHight = CGRectGetMaxY(_picF) + padding;

    } else {

        _cellHight = CGRectGetMaxY(_textF) + padding;

    }

}

4.frame模型數據的初始化已經采取懶加載的方式(每一個cell對應的frame模型數據只加載一次)

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    YUStatusCellFrame *frame = self.statusFrames[indexPath.row];

    return frame.cellHight;

}

最后這樣返回每個cell的高度就可以啦

 

最后說一下彈窗事件,我做了個點擊cell彈出對話框修改用戶名的功能

 

iOS8以前,彈窗需要遵守協議還有實現按鈕的點擊處理,比較麻煩,而iOS8以后可以在一個方法里搞定,而且不用遵守協議

// cell點擊事件監聽

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    YUStatusCellFrame *statusFrame = self.statusFrames[indexPath.row];

    NSString *name = statusFrame.status.name;

    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"博主" message:nil preferredStyle:UIAlertControllerStyleAlert];

    [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {

        textField.text = name;

    }];

    UIAlertAction *modify = [UIAlertAction actionWithTitle:@"修改" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {

        statusFrame.status.name = alert.textFields[0].text;

        //修改后計算出新的name所占frame的大小

        CGSize newSize = [statusFrame.status.name boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : YUTextFont} context:nil].size;

        statusFrame.nameF = CGRectMake(CGRectGetMinX(statusFrame.nameF) , CGRectGetMinY(statusFrame.nameF), newSize.width, newSize.height);

        //重新加載該行數據

        [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight];

    }];

    UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {

    }];

    [alert addAction:modify];

    [alert addAction:cancel];

    [self presentViewController:alert animated:YES completion:nil];

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM