仿呱呱购物流时间轴效果(swift):
在呱呱购App二期的优化过程中,发现物流信息详情界面显示的不太友好,由此,修改了部分代码使cell实现自适应,完美显示具体的物流轨迹。我使用了快递鸟的api,将这部分的view,model及Ctrl代码抽离出来,并在github上面开源,并在此贴出来:
先说下思路: 由分析可知,该界面整个为一个Tableview,上部分的相关信息为tableview的headerview,下部分的内容为tableview的Cells,而cell中又分为 4个view+2个label,先看cell左边的时光轴,实质上是三个view(line1V + 圆点View + line2V)拼接而成,只是第一个cell中渲染界面时更改相应的约束(如圆点View变大,颜色变红),再看右边:其实由两个label+1个分割线View组合而成。注意:由于显示物流信息轨迹的图片得根据后台返回的信息多少来自适应布局,其属性的
label.numberOfLines = 0,
整个tableview得实现自适应:应添加如下属性:
tableView?.estimatedRowHeight = 160
tableView?.rowHeight = UITableViewAutomaticDimension
image.png
1. 安装必要的插件库:
platform :ios, ’10.0’
target ‘GoodInfos’ do
use_frameworks!
pod ’SnapKit’ //自动布局库
pod ‘SDWebImage’ //渲染图片库
pod 'SwiftyJSON' //解析Json库
pod 'Alamofire' //网络请求库
pod 'SwiftIconFont' //字体图标库
pod 'MBProgressHUD' //菊花提示库
pod "MJRefresh" //下拉刷新库
end
2. Model(数据模型):
//物流信息
struct ExpressMs {
var AcceptStation:String
var AcceptTime:String
}
//基本信息
struct ShipMs {
//订单号
var order_num: String
//物流公司
var order_logistics_company:String
//物流单号
var order_logistics_num: String
//缩略图
var thumbnail_S:String
//订单状态
var order_status:String
}
3.View(视图)的布局:
3.1tableCell中的部分代码:
import UIKit
import SnapKit
class ExpressTbCells: UITableViewCell {
//时间轴线上的图标
var timeLineIcon: UIView = UIView()
//物流信息显示文本标签
var infoLabel: UILabel = UILabel()
//消费条目文本标签
var timeLabel: UILabel = UILabel()
//图标上半部分的时间线
var forepartTimeLineLabel: UIView = UIView()
//图标下半部分的时间线
var backpartTimeLineLabel: UIView = UIView()
var lineView:UIView = UIView()
//时间线离左右的横向间距
let horizontalGap: CGFloat = 15
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setSubviews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setSubviews(){
//在此布局,具体代码请见github
}
}
3.2View中贴tableView创建代码:
import UIKit
import SnapKit
import SDWebImage
import MJRefresh
protocol ExpressVDel: class {
//去刷新
func toFresh(expressV : ExpressV) //定义代理,到控制器中去执行相应的刷新代码逻辑
}
class ExpressV:UIView{
//没有数据时展示的View
var noDataV = UIView()
var expressMs : [ExpressMs]? {
didSet {
tableView?.reloadData()
}
}
// 顶部刷新 Mjrefresh
let freshH = MJRefreshNormalHeader()
weak var expressVDel : ExpressVDel?
var tableView:UITableView?
func setTb(){
//创建tableview
self.tableView = UITableView(frame: CGRect(x:0,y:StatusBarAndNavigationBarH,width:ScreenInfo.width,height:ScreenInfo.height - StatusBarAndNavigationBarH - TabbarSafeBotM), style:.grouped)
self.tableView?.delegate = self
self.tableView?.dataSource = self
//创建一个重用的单元格
self.tableView?.register(ExpressTbCells.self,
forCellReuseIdentifier: "SwiftCell")
addSubview(self.tableView!)
tableView?.estimatedRowHeight = 160 //将tableview的estimatedRowHeight设大一点
tableView?.rowHeight = UITableViewAutomaticDimension
tableView?.separatorStyle = UITableViewCellSeparatorStyle.none
freshH.setRefreshingTarget(self, refreshingAction: #selector(self.freshFunc))
tableView?.mj_header = freshH
}
setTb()
}
extension ExpressV:UITableViewDataSource, UITableViewDelegate {
//在本例中,只有一个分区
func numberOfSections(in tableView: UITableView) -> Int {
return 1;
}
//返回表格行数(也就是返回控件数)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
noDataV.isHidden = false
guard let lengths = self.expressMs else{return 0}
return lengths.count
}
//头视图
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
return headerView
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 0.000001;
}
//创建各单元显示内容(创建参数indexPath指定的单元)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
noDataV.isHidden = true
let identifier = "SwiftCell"
var cell = tableView.cellForRow(at: indexPath)
as? ExpressTbCells //注意在此不能重用单元格,否则第10个Cell与第0个cell显示一致,会冲突
if cell == nil{
cell = ExpressTbCells(style: .default, reuseIdentifier: identifier)
cell?.selectionStyle = .none
}
//除去选中时的颜色
cell?.selectionStyle = .none
//获取记录
let datas = self.expressMs![indexPath.row] as ExpressMs
//设置到达站点
cell?.infoLabel.text = "\(datas.AcceptStation)"
//设置到达站点的时间
cell?.timeLabel.text = datas.AcceptTime
self.visited[indexPath.row] = true
if indexPath.row == 0 {
cell?.timeLineIcon.layer.cornerRadius = (setHeight(size: 5))
cell?.timeLineIcon.backgroundColor = UIColor.red
cell?.infoLabel.textColor = UIColor.colorWithCustom(242, g: 71, b: 30)
cell?.timeLabel.textColor = UIColor.colorWithCustom(242, g: 71, b: 30)
cell?.timeLineIcon.snp.updateConstraints { (make) -> Void in
make.width.height.equalTo(setHeight(size: 10))
}
cell?.forepartTimeLineLabel.backgroundColor = UIColor.white
}
if indexPath.row == 4{
cell?.infoLabel.font = UIFont.UiFontSize(size: 48)
}
if indexPath.row == self.expressMs!.count - 1{
cell?.lineView.backgroundColor = UIColor.colorWithCustom(241, g: 243, b: 246,alpha: 1)
}
return cell!
}
}
extension ExpressV{
@objc func freshFunc(){
expressVDel?.toFresh(expressV : self)
}
}
4. VM(视图模型):
import UIKit
import SwiftyJSON
class ExpressVM: NSObject {
var shipMs:[ShipMs] = [GoodInfos.ShipMs(order_num: "1171130968809631905", order_logistics_company: "圆通", order_logistics_num: "051711301643554785", thumbnail_S: "https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=3654744077,710703022&fm=173&s=7BAE34621142ECE856D81DC70000E0B1&w=218&h=146&img.JPEG",order_status: "2")]
var expressMs:[ExpressMs] = [ExpressMs]()
//发起网络请求:
func getDeliver(paras : [String : Any]? = nil, _ finishCallback : @escaping (_ result: Any) -> ()) {
AlamofireStart.requestData(.GET, options:"http://139.199.169.203/ApiSearch.php",parameters:paras) { (result) in
STLog(JSON(result)["Success"])
if JSON(result)["Success"] == true{
for i in 0..<Int(JSON(result)["Traces"].count){
let datas1 = ExpressMs(AcceptStation: "\(JSON(result)["Traces"][Int(JSON(result)["Traces"].count)-i-1]["AcceptStation"])",AcceptTime: "\(JSON(result)["Traces"][Int(JSON(result)["Traces"].count)-i-1]["AcceptTime"])");
self.expressMs.append(datas1)
}
}
finishCallback(result)
}
}
}
5. Ctrl(控制器中逻辑):
import UIKit
import SDWebImage
import SnapKit
import SwiftIconFont
import SwiftyJSON
//物流信息
class ExpressVC: UIViewController,UIGestureRecognizerDelegate {
var expressMs:[ExpressMs] = [ExpressMs]()
fileprivate lazy var expressVM : ExpressVM = ExpressVM()
// 2.创建Group
let dGroup = DispatchGroup()
lazy var expressV: ExpressV = {[weak self] in
let titleFrame = CGRect(x: 0, y: 0, width: ScreenInfo.width, height: ScreenInfo.height)
let expressV = ExpressV(frame: titleFrame)
expressV.expressVDel = self
return expressV
}()
override func viewDidLoad() {
super.viewDidLoad()
self.setUpDatas()
expressVM.expressVMDel = self
}
}
extension ExpressVC:ExpressVMDel,ExpressVDel{
//封装一波
func setUpDatas(){
self.view.addSubview(expressV)
self.expressVM.expressMs.removeAll()
self.expressV.expressMs?.removeAll()
HudTips.showHUD(ctrl: self)
dGroup.enter()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now(), execute: {
let datas: ShipMs = self.expressVM.shipMs[0] as ShipMs
self.expressV.picUrls = datas.thumbnail_S
self.expressV.status_Vals = datas.order_status
self.expressV.whichE_Vals = datas.order_logistics_company
self.expressV.orderNum_Vals = datas.order_num
self.expressV.eNum_Vals = datas.order_logistics_num
self.dGroup.leave()
})
dGroup.enter()
expressVM.getDeliver(){(result) in
self.expressV.expressMs = self.expressVM.expressMs
self.dGroup.leave()
}
}
func toFresh(expressV:ExpressV){
//下拉刷新
}
}
6. 总结:
- 此例子另加mjRefesh下拉刷新,MBProgressHUD异步请求提示框,Toaster提示等组件,tableview没有数据时的占位图
- 快递鸟Api phpDemo下载地址:http:/139.199.169.203/KdApiSearchDemo.zip
- 附上GitHub地址:https://github.com/stoneUoU/GoodInfos
-
最终效果展示:
最终效果
网友评论