IOS開發學習筆記043-QQ聊天界面實現


QQ聊天界面實現

效果如下:

屏幕快照 2015 06 07 15 26 21 

 實現過程:

 1、首先實現基本界面

        頭像使用 UIImageView :

        文字消息使用 UIButton

        標簽使用 UILable :水平居中

        所有元素在一個cell中,在加載cell時進行判斷顯示和隱藏。

        合理設置各個控件之間的約束關系。主要是UIIimageVIew和UIButton頂部對齊,間距為10。UIButton的寬度設置一個約束范圍,比如說 (>=60 &&  <=300);

        底部添加一個UIView ,添加輸入框等。

       屏幕快照 2015 06 07 15 37 51 

2、創建模型文件

      所有元素在一個cell中,在加載cell時進行判斷顯示和隱藏。

       按照message.plist文件內容添加需要的屬性,然后添加一個cellHeight屬性計算cell高度,和一個決定是否顯示時間到cell得屬性hideTime。

#import <UIKit/UIKit.h>

 

// 枚舉類型,

typedefenum{

    SLQMessageTypeMe = 0,

    SLQMessageTypeOther = 1

}SLQMessageType;

  

@interface SLQMessage : NSObject

/*內容*/

@property (strong, nonatomic) NSString *text;

/*時間*/

@property (strong, nonatomic) NSString *time;

/*類型*/

@property (assign, nonatomic) SLQMessageType type;

 

/*cellHeight*/

@property (assign, nonatomic) CGFloat cellHeight;

/*是否隱藏時間*/

@property (assign, nonatomic,getter=isHideTime) BOOL hideTime;

 

+ (instancetype)MessageWithDict:(NSDictionary *)dict;

  

@end

 

實現文件

 

#import "SLQMessage.h"

 

@implementation SLQMessage

 

+(instancetype)MessageWithDict:(NSDictionary *)dict

{

    SLQMessage *message = [[SLQMessage alloc] init];

    [message setValuesForKeysWithDictionary:dict];

    return  message;

}

 

@end

        這里需要注意的就是枚舉類型的使用,如果在一個類中要定義枚舉類型,那么命名規則就是:

        以類名開頭后面直接跟操作標識;如 SLQMessage + Type

3、實現對cell操作的封裝

#import <UIKit/UIKit.h>

@classSLQMessage;

@interface SLQMessageCell : UITableViewCell

 

/*模型對象*/

@property (strong, nonatomic) SLQMessage *message;

 

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

 

@end

對tableView的每一個控件拖線建立關聯。然后重寫setter方法,對控件進行賦值。

 

#import "SLQMessageCell.h"

#import "SLQMessage.h"

 

//define this constant if you want to use Masonry without the 'mas_' prefix

#define MAS_SHORTHAND

//define this constant if you want to enable auto-boxing for default syntax

#define MAS_SHORTHAND_GLOBALS

#import "Masonry.h"

 

 

@interfaceSLQMessageCell ()

@property (weak, nonatomic) IBOutletUILabel *timeLable;

@property (weak, nonatomic) IBOutletUIButton *meBtn;

@property (weak, nonatomic) IBOutletUIImageView *meImage;

@property (weak, nonatomic) IBOutletUIButton *otherBtn;

@property (weak, nonatomic) IBOutletUIImageView *otherImage;

@end

 

@implementation SLQMessageCell

 

// 重寫setter方法

 

- (void)setMessage:(SLQMessage *)message

{

    _message = message;

   

    self.backgroundColor = [UIColorbrownColor];

    if(message.isHideTime) // 隱藏時間

    {

        self.timeLable.hidden = YES;

        [self.timeLableupdateConstraints:^(MASConstraintMaker *make) {

            make.height.equalTo(0); // 高度為0

        }];

    }

    else

    {

        self.timeLable.text = message.time;

        self.timeLable.hidden = NO;

        [self.timeLableupdateConstraints:^(MASConstraintMaker *make) {

            make.height.equalTo(22);

        }];

    }

    

    if (message.type == SLQMessageTypeMe)

    {

        [selfsetShowBtn:self.meBtnWithShowImage:self.meImageWithHideBtn:self.otherBtnWithHideImage:self.otherImage];

    }

    if (message.type == SLQMessageTypeOther)

    {

        [selfsetShowBtn:self.otherBtnWithShowImage:self.otherImageWithHideBtn:self.meBtnWithHideImage:self.meImage];

    }

}

        因為每次顯示cell都要進行計算,將cell的顯示封裝到方法中。

// 顯示隱藏控件並計算控件的高度

- (void)setShowBtn:(UIButton *)showBtn WithShowImage:(UIImageView *)showImage WithHideBtn:(UIButton *)hideBtn WithHideImage:(UIImageView *)hideImage

{

    [showBtn setTitle:self.message.textforState:UIControlStateNormal];

 

    // 隱藏其他

    hideBtn.hidden = YES;

    hideImage.hidden = YES;

    // 顯示自己

    showBtn.hidden = NO;

    showImage.hidden = NO;

    

    // 強制更新

    [selflayoutIfNeeded];

    // 更新約束,設置按鈕的高度就是textLable的高度

    [showBtn updateConstraints:^(MASConstraintMaker *make) {

        CGFloat buttonH = showBtn.titleLabel.frame.size.height;// 

        make.height.equalTo(buttonH);

    }];

    // 強制更新

    [selflayoutIfNeeded];

    CGFloat btnMaxY = CGRectGetMaxY(showBtn.frame);

    CGFloat imageMaxY = CGRectGetMaxY(showImage.frame);

    // 設置cell高度

    self.message.cellHeight = MAX(btnMaxY, imageMaxY) + 10;

}

    其他方法和以往一樣

+ (instancetype)cellWithTableView:(UITableView *)tableView

{

    SLQMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:@"message"];

    return cell;

}

- (void)awakeFromNib {

    // Initialization code

    // 多行顯示

     self.meBtn.titleLabel.numberOfLines=0;

     self.otherBtn.titleLabel.numberOfLines=0;

}

 

4、接下來說說按鈕背景的問題

        按鈕背景默認填充整個按鈕,但是默認情況下的填充效果不是很好。

如下代碼:

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

    imageView.frame = CGRectMake(10, 10, 300, 200);

    UIImage *image = [UIImage imageNamed:@"chat_send_nor"];

 

    // 方法1 ,設置拉伸間距,默認拉伸中心1*1像素

    //image = [image stretchableImageWithLeftCapWidth:image.size.width * 0.5 topCapHeight:image.size.height * 0.5];

    // 方法2 設置邊界

    UIEdgeInsets edge = UIEdgeInsetsMake(50, 40, 40, 40);

    //image = [image resizableImageWithCapInsets:edge ];

    

    // UIImageResizingModeStretch 拉伸模式

    // UIImageResizingModeTile 填充模式

    image = [image resizableImageWithCapInsets:edge resizingMode:UIImageResizingModeStretch];

    // 方法3

    // images.xcassets中對圖片進行設置

    

    imageView.image = image;

    [self.view addSubview:imageView];

 

    // 對比圖片

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

    imageView1.frame = CGRectMake(10, 210, 300, 200);

    UIImage *image1 = [UIImage imageNamed:@"chat_send_nor"];

    imageView1.image = image1;

 

    [self.view addSubview:imageView1];

會出現以下效果,默認是下邊的圖片,所以有必要對圖片進行拉伸。

         屏幕快照 2015 06 07 16 21 42

      其中方法3的設置是將圖片導入Image.xcassets中后選中圖片設置。

        屏幕快照 2015 06 06 14 30 58

       可以通過代碼設置按鈕的內間距

    // 可以這樣設置內間距

    UIEdgeInsets edge = UIEdgeInsetsMake(15151515);

 

    [showBtn setTitleEdgeInsets:edge];

       或者直接在按鈕的屬性里設置

         屏幕快照 2015 06 07 16 08 34

        設置過間距后,就可以計算btn的高度時,因為textlable的高度不固定,所以讓btn的高度等於textLable 的高度。但是又因為按鈕背景圖片的邊緣有一部分是透明的,如下:紅色是按鈕,藍色是圖片。  

        屏幕快照 2015 06 07 16 04 27   

       所以顯示文字高度會,這里對其按鈕高度 + 30,而textLable默認會水平垂直居中。

        屏幕快照 2015 06 07 16 07 07

5、在控制器中得實現方法和以往的一樣

        只需要在這里判斷以下消息顯示的時間是否一致,如果一致就隱藏。

- (NSMutableArray *)messages

{

    if (_messages == nil)

    {

        NSArray *dictArray = [NSArrayarrayWithContentsOfFile:[[NSBundlemainBundle] pathForResource:@"messages.plist"ofType:nil]];

        NSMutableArray *tempArray = [NSMutableArrayarray];

        // 記錄上一個message,判斷是否顯示時間

        SLQMessage *lastMessage = nil;

        for (NSDictionary *dict in dictArray)

        {

            SLQMessage *message = [SLQMessage MessageWithDict:dict];

            message.hideTime = [message.time isEqualToString:lastMessage.time];

            [tempArray addObject:message];

            // 重新賦值

            lastMessage = message;

        }

        _messages = tempArray;

    }

    return_messages;

 

}

 

 

- (void)viewDidLoad {

    [superviewDidLoad];

}

/**

 *  tableView 行數

 */

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

{

    //NSLog(@"%zd",self.messages.count);

    returnself.messages.count;

}

/**

 *  設置每一個cell

 */

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

{

    SLQMessageCell *cell = [SLQMessageCellcellWithTableView:tableView];

    

    cell.message = self.messages[indexPath.row];

    

    return  cell;

}

/**

設置cell高度

*/

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

{

    SLQMessage *message = self.messages[indexPath.row];

    return message.cellHeight;

}

/**

 *  給出預估高度

 */

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath

{

    return  200;

}

 

@end

 
 
總結:
這是一種方法,還有其他的實現方法,接下來嘗試一下。
 

5、用兩個cell實現界面

屏幕快照 2015 06 07 17 32 08

只需改動一些代碼就行。
        1、改動每個cell的標志 一個是me,一個是other
        2、修改setter方法

// 重寫setter方法

- (void)setMessage:(SLQMessage *)message

{

    _message = message;

   

    self.backgroundColor = [UIColorbrownColor];

    if(message.isHideTime) // 隱藏時間

    {

        self.timeLable.hidden = YES;

        [self.timeLableupdateConstraints:^(MASConstraintMaker *make) {

            make.height.equalTo(0);

        }];

    }

    else

    {

        self.timeLable.text = message.time; // 顯示時間

        self.timeLable.hidden = NO;

        [self.timeLableupdateConstraints:^(MASConstraintMaker *make) {

            make.height.equalTo(22);

        }];

    }

    //

    [self.contentBtnsetTitle:message.textforState:UIControlStateNormal];

    // 強制布局

    [selflayoutIfNeeded];

    // 添加約束

    [self.contentBtnupdateConstraints:^(MASConstraintMaker *make) {

        CGFloat textLableHeight = self.contentBtn.titleLabel.frame.size.height + 30;

        make.height.equalTo(textLableHeight);

    }];

    

    [selflayoutIfNeeded];

    CGFloat btnMaxY = CGRectGetMaxY(self.contentBtn.frame);

    CGFloat iconMaxY = CGRectGetMaxY(self.iconImage.frame);

    message.cellHeight = MAX(btnMaxY, iconMaxY);

}

        3、修改返回cell對象的方法,傳入一個message用來判斷是哪個cell

/**

 *  返回cell對象

 */

+ (instancetype)cellWithTableView:(UITableView *)tableView andMessage:(SLQMessage *)message

{

    NSString *ID =  (message.type == SLQMessageTypeMe)?@"me":@"other";

    SLQMessageCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];

    return cell;

}  

      4、在控制器中設置如下

/**

 *  設置每一個cell

 */

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

{

    // 獲取一個cell,根據類型

    SLQMessageCell *cell = [SLQMessageCell cellWithTableView:tableView andMessage:self.messages[indexPath.row]];

    

    cell.message = self.messages[indexPath.row];

    return  cell;

}

 
好了,效果一樣。
 
 

更新: 滾動到最新的一行

響應一個發送按鈕,點擊發送按鈕后獲取文本框數據,並顯示到tableView中。
滾動到最新一行
 
發送按鈕如下
 

/**

 *  發送信息

 */

- (IBAction)sendMessage:(id)sender

{

    // 獲取文字內容

    NSString *message = self.textField.text;

    SLQMessageType type = (arc4random_uniform(2));

    // 更新模型數據

    SLQMessage *mess = [[SLQMessage alloc] init];

    mess.time = @"2015-11-23";

    mess.text = message;

    mess.type = type;

    // 設置模型數據,添加到數組

    [self.messages addObject:mess];

    

    // 刷新表格

    [self.tableViewreloadData];

    // 滾動到底部方法

    [selfscrollToBottom];

}

 
滾動底部方法
 

// 滾動到底部

- (void)scrollToBottom

{

    CGFloat yOffset = 0;

    // 如果tableView的高度大於tableView的自有有高度,y軸偏移量就等於contentSize - bounds

    if (self.tableView.contentSize.height > self.tableView.bounds.size.height) {

        yOffset = self.tableView.contentSize.height - self.tableView.bounds.size.height;

    }

    // 設置偏移量為最底部

    [self.tableViewsetContentOffset:CGPointMake(0, yOffset) animated:NO];

}


免責聲明!

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



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