下面討論一下微信朋友圈的實現方式。先分析結構,和佈局。
微信朋友圈,看起來很有秩序,而且滑動的時候也不卡,應用的非常好。對於微信朋友圈的樣式,我們可以大致分成以下6種類型。
- 純文字類型
- 單張圖得顯示
- 多張圖得顯示(按照九宮格排練)
- 鏈接類型
- 視頻類型
- 廣告類型
那麼我們需要分析每種類型的佈局有什麼共同點,和不同點。
每種類型都是有一個頭像和一個姓名,這個是必須的,我們可以封裝到父類裏。每種類型,都可以附帶文字說明,也可以不帶,我們也可以封裝到父類裏,但是這個不是必須要創建的時候,就生成的,而是根據當前有沒有文字說明來生成的,所有說他應該是動態生成的,對於複用我們可以用隱藏。最好不要從父類移除。
還有就是評論,每種類型都可以評論。那麼我們就要把評論封裝到一起。也可以封裝到父類。對於顯示時間這個也是必須的我們也可以封裝到父類裏。
那麼我們可以看到如下結構:
以一個多張圖類型,我們分析一下,紅框算一個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<UITouch>, with event: UIEvent?) {
fetchImageIndex(touches, isEnd: false);
}
func fetchImageIndex(_ touches: Set<UITouch>,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<UITouch>, with event: UIEvent?) {
fetchImageIndex(touches, isEnd: true);
}
override func touchesCancelled(_ touches: Set<UITouch>, 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<UITouch>, 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