iOSiOS开发进阶首页投稿(暂停使用,暂停投稿)

局域网内端到端的聊天项目(四)

2017-11-29  本文已影响147人  _令_狐_冲_
效果图: iPod.gif iPhone.gif
 1. 图片的来源相册/相机
 2. 该项目没有服务端也没有云存储所以图片需要保存至本地 (ps:聊天记录的保存准备在最后在建立数据库)
 3. 图片的发送策略
 4. 图片的接收策略
IMG_2437.GIF
一.键盘右边的+按钮的实现(ESAddOpationView)
//  Created by 张海军 on 2017/11/27.
//  Copyright © 2017年 baoqianli. All rights reserved.
//  加号按钮的view

#import <UIKit/UIKit.h>

@class OpationItem;

typedef NS_ENUM(NSInteger, OpationItem_type)
{
    OpationItem_image = 0 // 照片选择
};


@interface ESAddOpationView : UIView
/// 功能选项数组
@property (nonatomic, strong) NSArray<OpationItem*>* opationItem;
/// 选中回调
@property (nonatomic, copy) void(^selectedOpationHandle)(OpationItem_type type);
+ (instancetype)addOpationView;

@end

// 选项模型
@interface OpationItem : NSObject
/// 选项名称
@property (nonatomic, copy) NSString *itemName;
/// 选项图片名
@property (nonatomic, copy) NSString *itemIconName;
/// 类型
@property (nonatomic, assign)  OpationItem_type type;

+ (instancetype)opationItemWithName:(NSString *)itemName iconName:(NSString *)iconName type:(OpationItem_type)type;
@end

// 自定义选项按钮
@interface OpationButton : UIButton

@end
与表情键盘的相互切换
// 1.表情键盘(ESEmoticonView)  功能选项(ESAddOpationView) 加到通过一个父view(inputView) 设置键盘的inputView 通过显示隐藏来实现
self.inputTextView.inputView = inputView;

// 表情按钮的点击
- (void)emoticonButtonDidClick:(UIButton *)button
{
    if (self.addButton.selected) {
        self.addButton.selected = NO;
    }
    self.addOpationView.hidden = YES;
    self.emoticonView.hidden = NO;
    if (!self.emoticonView.superview) {
        [self.inputView addSubview:self.emoticonView];
    }
    [self changeInputView:self.inputView selected:button.selected];
    button.selected = !button.selected;
}
// 功能选项按钮点击
- (void)addButtonDidClick:(UIButton *)button{
    if (self.emoticonButton.selected) {
        self.emoticonButton.selected = NO;
    }
    self.emoticonView.hidden = YES;
    self.addOpationView.hidden = NO;
    if (!self.addOpationView.superview) {
        [self.inputView addSubview:self.addOpationView];
    }
    [self changeInputView:self.inputView selected:button.selected];
    button.selected = !button.selected;
}

二.图片选择及回调
#import <Foundation/Foundation.h>
#import <TZImagePickerController.h>

typedef void(^pickerFinishBlock)(NSArray<UIImage *> *photos,NSArray *assets );

@interface PickerImageVideoTool : NSObject
+ (instancetype)sharePickerImageVideoTool;
- (void)showImagePickerWithMaxCount:(NSInteger)maxCount completion:(pickerFinishBlock)finishBlock;
@end
三.触发图片选择的回调仍有 ESKeyBoardToolView 来触发
#import <UIKit/UIKit.h>
#import "ESAddOpationView.h"

/// 输入框最多显示多少行
static NSInteger maxLines = 4;
/// 输入框的高度
static CGFloat const TitleViewHeight = 44.0;

typedef NS_ENUM(NSInteger, ESKeyBoardToolView_type)
{
    ESKeyBoardToolView_typeEmoticon = 0,       // 表情按钮的点击
    ESKeyBoardToolView_typeAdd                 // 加号按钮的点击
};

@class ESKeyBoardToolView;

@protocol ESKeyBoardToolViewDelegate <NSObject>

@optional
/// 加号选项view的点击
- (void)ESKeyBoardToolViewAddOpationDidSelected:(ESKeyBoardToolView *)view withType:(OpationItem_type)type;
/// 点击发送按钮
- (void)ESKeyBoardToolViewSendButtonDidClick:(ESKeyBoardToolView *)view message:(NSString *)message;
/// 当正在编辑文字时view的Y值变化
- (void)ESKeyBoardToolViewDidEditing:(ESKeyBoardToolView *)view  changeY:(CGFloat)yValue;
/// 结束编辑回调
- (void)ESKeyBoardToolViewDidEndEdit:(ESKeyBoardToolView *)view;
@end
四. ESKeyBoardToolViewDelegate 的代理实现
#pragma mark - ESKeyBoardToolViewDelegate
- (void)ESKeyBoardToolViewAddOpationDidSelected:(ESKeyBoardToolView *)view withType:(OpationItem_type)type{
    switch (type) {
        case OpationItem_image:{
            [self sendImageOrVideo];
        }
            break;
            
        default:
            break;
    }
  }
}

图片选择完成后的回调
- (void)sendImageOrVideo{
    WS(weakSelf);
    [[PickerImageVideoTool sharePickerImageVideoTool] showImagePickerWithMaxCount:1 completion:^(NSArray<UIImage *> *photos, NSArray *assets) {
        NSInteger count = assets.count;
        id objc = nil;
        for (NSInteger i = 0; i < count; i++) {
            objc = assets[i];
            if (![objc isKindOfClass:[PHAsset class]]) {
                continue;
            }
            PHAsset *asset = (PHAsset *)objc;
            ChatMessageModel *messageM = [ChatMessageModel new];
            messageM.isFormMe = YES;
            messageM.userName = [UIDevice currentDevice].name;
            messageM.asset = asset;
            messageM.fileName = [ZPPublicMethod getAssetsName:asset only:YES];
            messageM.temImage = photos[i];
            if (asset.mediaType == PHAssetMediaTypeImage){
                messageM.chatMessageType = ChatMessageImage;
                messageM.fileSize = UIImagePNGRepresentation(messageM.temImage).length;
                // 本地存储
                SDImageCache *cache = [SDImageCache sharedImageCache];
                [cache storeImage:messageM.temImage forKey:messageM.fileName toDisk:YES completion:^{
                    messageM.mediaMessageUrl = [NSURL fileURLWithPath:[cache defaultCachePathForKey:messageM.fileName]];
                }];
               // 发送图片
                [weakSelf sendMessageWithItem:messageM];
            }else if (asset.mediaType == PHAssetMediaTypeAudio){
               // 预留处理
                messageM.chatMessageType = ChatMessageAudio;
            }else if (asset.mediaType == PHAssetMediaTypeVideo) {
                // 预留处理
                messageM.chatMessageType = ChatMessageVideo;
            }
        }
        
    }];
五.图片的发送
消息体的发送
/// 发送数据
- (void)sendMessageWithItem:(ChatMessageModel *)item{
    self.currentSendItem = item;
    NSData *textData = [self creationMessageDataWithItem:item];
    [self writeMediaMessageWithData:textData];
}

// 创建消息体
- (NSData *)creationMessageDataWithItem:(ChatMessageModel *)item{
    NSMutableDictionary *messageData = [NSMutableDictionary dictionary];
    messageData[@"fileName"] = item.fileName;
    messageData[@"userName"] = item.userName;
    messageData[@"chatMessageType"] = [NSNumber numberWithInt:item.chatMessageType];
    messageData[@"fileSize"] = [NSNumber numberWithInteger:item.fileSize];
    if (item.chatMessageType == ChatMessageText) {
        messageData[@"messageContent"] = item.messageContent;
    }else if (item.chatMessageType == ChatMessageImage){
       // 等待接受字段
        item.isWaitAcceptFile = YES;
        messageData[@"isWaitAcceptFile"] = [NSNumber numberWithBool:YES];
    }
    NSString *bodStr = [NSString hj_dicToJsonStr:messageData];
    return [bodStr dataUsingEncoding:NSUTF8StringEncoding];
}

// 图片或者视频文件传输
- (void)imageOrVideoFileSend:(ChatMessageModel *)sendItem{
    if (sendItem.chatMessageType == ChatMessageImage) {
        NSData *sendData = UIImagePNGRepresentation(sendItem.temImage);
        [self writeMediaMessageWithData:sendData];
    }else if (sendItem.chatMessageType == ChatMessageVideo){
        // 视频发送的处理
    }
    
}
文件发送中及发送完成的回调
// 分段传输完成后的 回调 
- (void)socket:(GCDAsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag {
    self.currentSendItem.upSize += partialLength;
    if ([self.delegate respondsToSelector:@selector(socketManager:itemUpingrefresh:)] && (tag==self.currentSendItem.sendTag)) {
        self.currentSendItem.isSending = YES;
        [self.delegate socketManager:self itemUpingrefresh:self.currentSendItem];
    }
}

// 文件传输完毕后的回调
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
    if (self.currentSendItem.sendTag == tag) {
        if (!self.currentSendItem.isWaitAcceptFile) {
            self.currentSendItem.temImage = nil;
            if ([self.delegate respondsToSelector:@selector(socketManager:itemUpFinishrefresh:)]) {
                [self.delegate socketManager:self itemUpFinishrefresh:self.currentSendItem];
            }
        }else{
            // 接下来需要传输文件
            self.currentSendItem.isWaitAcceptFile = NO; // 改变状态
            [self imageOrVideoFileSend:self.currentSendItem];
        }
    }
    [self.tcpSocketManager setAutoDisconnectOnClosedReadStream:YES];
}
文件的接收处理
/// 接收到消息
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
    NSString *readStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSDictionary *readDic = [readStr hj_jsonStringToDic];
    if ([readDic isKindOfClass:[NSDictionary class]]) {
        self.acceptItem = [ChatMessageModel mj_objectWithKeyValues:readDic];
        self.acceptItem.isFormMe = NO;
        self.acceptItem.finishAccept = self.acceptItem.chatMessageType != ChatMessageText ? NO : YES;
    }else if (self.acceptItem.isWaitAcceptFile) {
        self.acceptItem.finishAccept = NO;
        self.acceptItem.acceptSize += data.length;
        self.acceptItem.beginAccept = YES;
        if (!self.outputStream) {
            self.acceptItem.acceptFilePath = [self.dataSavePath stringByAppendingPathComponent:[self.acceptItem.fileName lastPathComponent]];
            self.acceptItem.mediaMessageUrl = [NSURL fileURLWithPath:self.acceptItem.acceptFilePath];
            self.outputStream = [[NSOutputStream alloc] initToFileAtPath:self.acceptItem.acceptFilePath append:YES];
            [self.outputStream open];
        }
        // 输出流 写数据
        NSInteger byt = [self.outputStream write:data.bytes maxLength:data.length];
        if (self.acceptItem.acceptSize >= self.acceptItem.fileSize) {
            self.acceptItem.finishAccept = YES;
            [self.outputStream close];
            self.outputStream = nil;
        }
    }
    if ([self.delegate respondsToSelector:@selector(socketManager:itemAcceptingrefresh:)]) {
        [self.delegate socketManager:self itemAcceptingrefresh:self.acceptItem];
    }
    
    [sock readDataWithTimeout:- 1 tag:0];
    
}
文件接收中及接收完成 table的展示
#pragma mark - SocketManagerDelegate
// 正在接受的文件回调
- (void)socketManager:(SocketManager *)manager  itemAcceptingrefresh:(ChatMessageModel *)acceptingItem{
    if (acceptingItem.finishAccept) {
        [self.messageItems addObject:acceptingItem];
        [self.tableView reloadData];
        [self scrollToLastCell];
    }else{
        // 刷新当前进度
    }
}
上一篇 下一篇

猜你喜欢

热点阅读