iOS 仿微信朋友圈实现

下面讨论一下微信朋友圈的实现方式。先分析结构,和布局。

微信朋友圈,看起来很有秩序,而且滑动的时候也不卡,应用的非常好。对于微信朋友圈的样式,我们可以大致分成以下6种类型。

  • 纯文字类型
  • 单张图得显示
  • 多张图得显示(按照九宫格排练)
  • 链接类型
  • 视频类型
  • 广告类型

那么我们需要分析每种类型的布局有什么共同点,和不同点。

每种类型都是有一个头像和一个姓名,这个是必须的,我们可以封装到父类里。每种类型,都可以附带文字说明,也可以不带,我们也可以封装到父类里,但是这个不是必须要创建的时候,就生成的,而是根据当前有没有文字说明来生成的,所有说他应该是动态生成的,对于复用我们可以用隐藏。最好不要从父类移除。

还有就是评论,每种类型都可以评论。那么我们就要把评论封装到一起。也可以封装到父类。对于显示时间这个也是必须的我们也可以封装到父类里。

那么我们可以看到如下结构:

iOS 仿微信朋友圈实现_第1张图片

 

以一个多张图类型,我们分析一下,红框算一个cell,只有黑框位置的内容是不一样的,所以我们只要把黑框以外的分装好做成基类,其他的类型都继承基类,这样我们就能减少布局的变化,并且还能更多的复用。

那么我们用MVC的模式来分析:

  • 模型
class JHSMessageModel: NSObject {

    var name = "";
    var modelType: MessageModelType!
    var contentText: String!
    
    init(type: MessageModelType) {
        super.init();
        modelType = type;
        self.configData();
    }
    func configData() -> Void {
        
    }
    
    private var _contextSize: CGSize!
    var contextSize: CGSize {
        if contentText == nil {
            return CGSize.zero;
        }
        if _contextSize != nil {
            return _contextSize;
        }
        
        _contextSize = contentText.textSize(size: CGSize(width: PengConfigParamter.maxWidth, height: CGFloat.greatestFiniteMagnitude), font: PengConfigParamter.textFont);
        return _contextSize;
    }
    var nameSize: CGSize {
        let size = name.textSize(size: CGSize(width: PengConfigParamter.maxWidth, height: CGFloat.greatestFiniteMagnitude), font: PengConfigParamter.textFont);
        return size;
    }
    
    var topHeight: CGFloat {
        let ht = (contextSize.height + 8) + nameSize.height + 12;
        return ht;
//        let ht = contextSize.height + nameSize.height + 14;
//        return ht < 60 ? 60 : ht;
    }
    private var _cHeight: CGFloat = 0;
    var commentHeight: CGFloat {
        if comment == nil || comment.count == 0 {
            return 0;
        }
        if _cHeight != 0 {
            return _cHeight;
        }
        
        for (_,item) in comment.enumerated() {
            _cHeight += (item.size.height + 4)
        }
        _cHeight += 20;
        return _cHeight;
    }
    
    var rowHeight: CGFloat {
        return topHeight + commentHeight + 6;
    }
    
    
    var comment: [JHSCommentModel]!

}

基类JHSMessageModel封装了共有的信息。姓名,类型,文字说明等属性,还有一些共有的方法。

  • 纯文字类型Model

只有文字类型的cell实际上和上边这个也是一样的,但是它是为了基类使用的,所有我们还需要定义它的子类。如下:

class JHSTextModel: JHSMessageModel {
    
}
  • 单张图得显示Model
class JHSImageModel: JHSMessageModel {
    var urlString = "";
    var image: UIImage!

    override var topHeight: CGFloat {
        let ht = super.topHeight;
        return ht + 200;
    }
    var supHeight: CGFloat {
        return super.topHeight;
    }
}
  • 多张图得显示(按照九宫格排练)Model
class JHSMutableImageModel: JHSMessageModel {
    var images: [UIImage]!
    var count = 8 {
        didSet{
            count = count > 8 ? 8: count;
            images.removeAll();
            for idx in 0...count {
                images.append(UIImage(named: "\(idx).jpg")!);
            }
        }
    }
    
    override func configData() {
        images = [UIImage]();
        for idx in 0...count {
            images.append(UIImage(named: "\(idx).jpg")!);
        }
    }
    override var topHeight: CGFloat {
        if images == nil {
            return super.topHeight;
        }
        let perWidth = (PengConfigParamter.maxWidth - 8 - 40) / 3 + 4;
        let row = ceil(Double(images.count) / 3.0)
        return super.topHeight + perWidth * CGFloat(row);
    }
    var supTopHeight: CGFloat {
        return super.topHeight;
    }
    
    var cellImageHeight: CGFloat {
        return topHeight - super.topHeight;
    }
}
  • 链接类型Model
class JHSNewModel: JHSMessageModel {
    
    override var topHeight: CGFloat {
        return super.topHeight + 48;
    }
    var link = "链接地址";
    var linkTitle = "标题";
    var lineImage : UIImage!;
    
}

对于model 基本的数据都已经有了,那么我们看看Model的view

先看基类的View:

class JHSGroupCell: UITableViewCell {
    var entity: JHSMessageModel!
    
    var leftImage: UIImageView!
    
    var nameLabel: UILabel!
    
    var contextLabel: UILabel!
    
    var tabBarView: JHSOperationView!
    
    var actionTarget: Any!
    var selector: Selector!
    
    
    fileprivate var commentView: JHSCommentItemView!
    var comments: [JHSCommentModel]! {
        didSet{
            configComment();
        }
    }
    
    
    var leftPointX: CGFloat {
        return leftImage.maxX + 10;
    }
    var topPointY: CGFloat {
        return nameLabel.maxY + 8;
    }
    var maxWidth: CGFloat {
        return PengConfigParamter.maxWidth
    }
    
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier);
        backgroundColor = UIColor.white;
        selectionStyle = .none;
        leftImage = createImageView(rect: .init(x: 10, y: 10, width: 40, height: 40));
        leftImage.contentMode = .scaleAspectFill;
        leftImage.clipsToBounds = true;
        leftImage.layer.masksToBounds = true;
        leftImage.layer.cornerRadius = 4;
        leftImage.image = UIImage(named: "1.jpg");
        
        
        nameLabel = createLabel(rect: .init(x: leftPointX, y: 10, width: 20, height: 14), text: "姓名");
        nameLabel.font = PengConfigParamter.textFont;
        nameLabel.textColor = PengConfigParamter.textColor;
        
        tabBarView = JHSOperationView(frame: .init(x: leftPointX, y: topPointY, width: PengConfigParamter.maxWidth, height: 30));
        addSubview(tabBarView);
        
        
        configSubView();
    }
    func configSubView() -> Void {
        
    }
    
    private func configComment() {
        if comments != nil && commentView == nil {
            commentView = JHSCommentItemView(frame: .init(x: leftPointX, y: nameLabel.maxY, width: maxWidth, height: 40));
            addSubview(commentView);
        }
        commentView?.comments = comments;
        commentView?.isHidden = comments == nil;
        
    }
    

    
    private func configContent(context: String?) -> Void {
        if context == nil {
            contextLabel?.text = "";
            return;
        }
        if contextLabel == nil {
            contextLabel = createLabel(rect: .init(x: leftPointX, y: topPointY, width: maxWidth, height: 20), text: "");
            contextLabel.numberOfLines = 0;
            contextLabel.font = PengConfigParamter.textFont;
            contextLabel.textColor = PengConfigParamter.textColor;
        }
        contextLabel.text = context;
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func configMessage(model: JHSMessageModel) -> Void {
        entity = model;
        nameLabel.text = model.name;
        configContent(context: model.contentText);
        comments = model.comment;
        
    }
    
    func addTarget(t: Any,sel: Selector) -> Void {
        self.actionTarget = t;
        selector = sel;
    }
    
    override func layoutSubviews() {
        super.layoutSubviews();
        
        if entity == nil {
            return;
        }
        
        var rect = nameLabel.frame;
        rect.size = entity.nameSize;
        nameLabel.frame = rect;
        
        if let label = contextLabel {
            rect = label.frame;
            rect.size = entity.contextSize;
            label.frame = rect;
        }
        
        if let cell = self as? JHSVideoCell {
            
            
            guard let imageModel = entity as? JHSVideoModel else{
                return;
            }
            rect = cell.backImage.frame;
            rect.origin.y = imageModel.supHeight;
            cell.backImage.frame = rect;
            
            rect = tabBarView.frame;
            rect.origin.y = cell.backImage.maxY;
            tabBarView.frame = rect;
            
            cell.backImage.image = UIImage(named: "1.jpg");
            
        }else if self is JHSTextCell {
            
            var rect = tabBarView.frame;
            rect.origin.y = entity.topHeight;
            tabBarView.frame = rect;
            
        }else if let cell = self as? JHSImageCell {
            
            if let imageModel = entity as? JHSImageModel {
                
                
                rect = cell.backImage.frame;
                rect.origin.y = imageModel.supHeight;
                cell.backImage.frame = rect;
                
                rect = tabBarView.frame;
                rect.origin.y = cell.backImage.maxY;
                tabBarView.frame = rect;
            }
        }else if let cell = self as? JHSMutableImageCell {
            
            
            
            guard let imgs = cell.mutableImages else {
                return;
            }
            let perWidth = (PengConfigParamter.maxWidth - 8 - 40) / 3;
            
            for item in imgs.enumerated() {
                let row = item.offset / 3;
                let column = item.offset % 3;
                let drawRect = CGRect(x: column.cgFloat * (perWidth + 4) + leftPointX, y: row.cgFloat * (perWidth + 4) + cell.topHeight, width: perWidth, height: perWidth);
                let img = cell.getImageBy(index: item.offset, rect: drawRect);
                img.image = item.element;
                img.isHidden = false;
                
            }
            if imgs.count < 9 {
                for idx in imgs.count...8 {
                    let img = cell.getImageBy(index: idx, rect: CGRect.zero);
                    img.isHidden = true;
                }
            }
            
            
            if let model = entity as? JHSMutableImageModel {
                var rect = tabBarView.frame;
                rect.origin.y = model.topHeight;
                tabBarView.frame = rect;
            }
            
        }else if let cell = self as? JHSNewCell {
            
            rect = cell.linkView.frame;
            rect.origin.y = entity.topHeight - 48;
            cell.linkView.frame = rect;
            
            rect = tabBarView.frame;
            rect.origin.y = entity.topHeight;
            tabBarView.frame = rect;
            
            
        }
        

        if let cView = commentView {
            rect = cView.frame;
            rect.origin.y = tabBarView.maxY;
            rect.size.height = entity.commentHeight;
            cView.frame = rect;
            
        }
        
    }
}
  • 纯文字类型View
class JHSTextCell: JHSGroupCell {
    
    
}
  • 单张图得显示View
class JHSImageCell: JHSGroupCell {
    
    var backImage: UIImageView!
    
    override func configMessage(model: JHSMessageModel) {
        super.configMessage(model: model);
        if let cModel = model as? JHSImageModel {
            backImage.image = cModel.image;
        }
    }
    
    
    override func configSubView() {
        backImage = createImageView(rect: .init(x: leftPointX, y: topPointY, width: 160, height: 200));
        addSubview(backImage);
        backImage.contentMode = .scaleAspectFill;
        backImage.clipsToBounds = true;
        
    }
}
  • 多张图得显示(按照九宫格排练)View
class JHSMutableImageCell: JHSGroupCell {
    
    var mutableImages: [UIImage]! {
        didSet{
            setNeedsLayout();
        }
    }
    var topHeight: CGFloat {
        if let model = entity as? JHSMutableImageModel {
            return model.supTopHeight;
        }
        return 0;
    }
    
    
    private var imageTag = 12345;

    override func configSubView() {
        backgroundColor = UIColor.white;
    }
    
    
    override func configMessage(model: JHSMessageModel) {
        super.configMessage(model: model);
        if let imgsModel = model as? JHSMutableImageModel {
            mutableImages = imgsModel.images;
        }
    }
    
    func getImageBy(index: Int,rect: CGRect) -> UIImageView {
        
        
        var img = viewWithTag(index + imageTag) as? UIImageView;
        if img == nil {
            
            img = createImageView();
            img?.tag = index + imageTag;
            img?.contentMode = .scaleAspectFill;
            img?.clipsToBounds = true;
        }
        if !rect.isNull {
            img?.frame = rect;
        }
        return img!;
    }
    
    override func addTarget(t: Any, sel: Selector) {
        
    }
    override func touchesBegan(_ touches: Set, with event: UIEvent?) {
        fetchImageIndex(touches, isEnd: false);
        
    }
    func fetchImageIndex(_ touches: Set,isEnd: Bool) -> Void {
        guard let point = touches.first?.location(in: self) else{
            return;
        }
        var index = -1;
        for idx in 0...8 {
            let rect = getImageBy(index: idx, rect: CGRect.null).frame;
            if !rect.isNull && rect.contains(point) {
                index = idx;
                break;
            }
        }
        if index != -1 ,let sel = selector , isEnd{
            let obs = actionTarget as? NSObject;
            obs?.perform(sel, with: [JHSCellKey.cell:self,JHSCellKey.imgIndex:index]);
        }
    }
    override func touchesEnded(_ touches: Set, with event: UIEvent?) {
        fetchImageIndex(touches, isEnd: true);
    }
    override func touchesCancelled(_ touches: Set, with event: UIEvent?) {
        fetchImageIndex(touches, isEnd: false);
    }

}
  • 链接类型View
class JHSNewCell: JHSGroupCell {
    
    var linkView: JHSLinkView!
    
    override func configSubView() {
        linkView = JHSLinkView(frame: .init(x: leftPointX, y: topPointY, width: PengConfigParamter.maxWidth, height: 48));
        addSubview(linkView);
    }
    override func configMessage(model: JHSMessageModel) {
        super.configMessage(model: model);
        if let lModel = model as? JHSNewModel {
            linkView.content = lModel.linkTitle;
            linkView.leftImg.image = lModel.lineImage;
        }
    }
    
    override func touchesEnded(_ touches: Set, with event: UIEvent?) {
        guard let point = touches.first?.location(in: self) else {
            return;
        }
        if linkView.frame.contains(point),let sel = selector {
            let obj = actionTarget as? NSObject;
            obj?.perform(sel, with: [JHSCellKey.cell:self]);
        }
    }
    
    
}

对于这样的设计我们就很好的运到实际工作共,那么我么也附上deom

demo地址链接。

你可能感兴趣的:(iOS,swift,朋友圈,swift,swift,4.2)