美文网首页
Swift Sku商品筛选实现

Swift Sku商品筛选实现

作者: fordG | 来源:发表于2020-08-05 10:17 被阅读0次
  • git链接

  • 效果图


    image.png
  • 先看下json数据


{
    "specList": [{
            "title": "颜色",
            "list": ["红色", "紫色"]
        },
        {
            "title": "套餐",
            "list": ["套餐一", "套餐二"]
        },
        {
            "title": "内存",
            "list": ["64G", "128G", "256G"]
        },
        {
            "title": "网络",
            "list": ["联通", "移动", "电信"]
        },
    ],
    "specCombinationList": [{
            "id": "1",
            "specs": ["紫色", "套餐一", "64G", "电信"],
            "count": 20
        },
        {
            "id": "2",
            "specs": ["紫色", "套餐一", "128G", "电信"],
            "count": 18
        },
        {
            "id": "3",
            "specs": ["紫色", "套餐二", "128G", "联通"],
            "count": 15
        },
        {
            "id": "4",
            "specs": ["红色", "套餐二", "256G", "移动"],
            "count": 10
        }
    ],
}

  • 转model用的swiftPackage 集成HandyJson

  • 获取json

import UIKit

class JsonUtils: NSObject {

    // 读取本地JSON文件
    static func redJson(name: String) ->  Dictionary<String, Any>{
        let path = Bundle.main.path(forResource: name, ofType: "json")
        let url = URL(fileURLWithPath: path!)
        // 带throws的方法需要抛异常
        do {
                  /*
                     * try 和 try! 的区别
                     * try 发生异常会跳到catch代码中
                     * try! 发生异常程序会直接crash
                     */
                let data = try Data(contentsOf: url)
                let jsonData:Any = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers)
            let jsonArr = jsonData as! Dictionary<String, Any>
                
            return jsonArr
        } catch let error as Error? {
            print("读取本地数据出现错误!",error ?? "")
            return Dictionary()
        }
    }

}
  • model
import UIKit
import HandyJSON

class SkuModel: HandyJSON{
    var specList: Array<SkuTopModel>?
    var specCombinationList: Array<SkuCondition>?
    var skuTopDict = NSMutableDictionary() //转换过后的顶点数据
    var skuConditionList = Array<Array<Int>>() //转换过后的条件数组
    
//    func mapping(mapper: HelpingMapper) {
//
//        mapper <<<
//        self.skuTopModelList <-- "specList"
//
//        mapper <<<
//        self.skuConditionList <-- "specCombinationList"
//    }
    
    required init() {
        
    }
}

class SkuTopModel: HandyJSON {
    var title: String? //顶点类别
    var list: Array<String>? //顶点数组
    required init(){}
}


//条件
class SkuCondition: HandyJSON {
    var id: String?
    var specs: Array<String>?
    var count: Int?
    
    required init(){}
}

class Condition {
    var x = 0
    required init(x: Int){
        self.x = x
    }
}
  • controller
class ViewController: UIViewController, UIGestureRecognizerDelegate {
    
    lazy var tableView: UITableView = {
        let table = UITableView(frame: CGRect(x: 0, y: 0, width: ScreenWidth, height: ScreenHeight), style: .plain)
        table.register(SkuCell.self, forCellReuseIdentifier: SkuCell.identifier)
        table.backgroundColor = UIColor.purple
        table.separatorStyle = .none
        return table;
    }()
    
    var skuCellHeight: CGFloat = 44
    
    var model: SkuModel?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        view.addSubview(tableView)
        tableView.dataSource = self
        tableView.delegate = self
        tableView.tableFooterView = UIView()
        
        loadData()
    }
    


    
    func loadData(){
        let sku = JsonUtils.redJson(name: "sku")
        
        if let skuModel = SkuModel.deserialize(from: sku){
            //解析数据成功, 初始化顶点坐标, 和对应的可选条件顶点的坐标的x 数组
            if let specList = skuModel.specList {
                for(index, spec) in specList.enumerated(){
                    if let list = spec.list {
                        for(i, model) in list.enumerated(){
                            let point = CGPoint(x: i, y: index)
                            skuModel.skuTopDict.setValue(point, forKey: model)
                        }
                    }
                }
            }
            
            //去point.x 匹配条件数组
            if let specCombinationList = skuModel.specCombinationList {
                for(_, obj) in specCombinationList.enumerated() {
                    var tmpArr = [Int]()
                    if let specs = obj.specs {
                        for(_, model) in specs.enumerated(){
                            if(skuModel.skuTopDict[model] != nil){
                                let point: CGPoint = skuModel.skuTopDict[model] as! CGPoint
                                tmpArr.append(Int(point.x))
                            }
                        }
                    }
                    skuModel.skuConditionList.append(tmpArr)
                }
            }
            
            model = skuModel
            tableView.reloadData()
        }
    }
    
    
    
    

}

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: SkuCell.identifier, for: indexPath) as? SkuCell
        cell?.model = model
        cell?.heightConfrim = {[unowned self] height in
            self.skuCellHeight = height
            self.tableView.reloadRows(at: [indexPath], with: .none)
        }
        return cell!
    }
}

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return skuCellHeight
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
    }
}
  • tableCell
import UIKit

typealias HeightConfirmBlock = (CGFloat)->(Void)

extension CGPoint: Hashable {
    
    public func hash(into hasher: inout Hasher){
        hasher.combine(self.x)
        hasher.combine(self.y)
    }
}

class SkuCell: UITableViewCell {
    
    static let identifier: String = "SkuCellIdentifier"
    var collection: UICollectionView?
    var heightConfrim: HeightConfirmBlock?
    var set: Set<String> = []
    var alreadys: Set<CGPoint> = []
    
    var model: SkuModel?{
        didSet{
            collection?.reloadData()
            
            DispatchQueue.main.async {
                print(self.collection!.collectionViewLayout.collectionViewContentSize.height, self.collection!.height)
                if(self.collection!.height != self.collection!.collectionViewLayout.collectionViewContentSize.height){
                    self.collection!.frame = CGRect(x: 0, y: 0, width: ScreenWidth, height: self.collection!.collectionViewLayout.collectionViewContentSize.height)
                    if let callBack = self.heightConfrim {
                        callBack(self.collection!.collectionViewLayout.collectionViewContentSize.height)
                    }
                }
            }
        }
    }
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        self.selectionStyle = .none
        
        let layout = UICollectionViewFlowLayout()
        layout.itemSize = CGSize(width: 80, height: 40)
        layout.scrollDirection = .vertical
        collection = UICollectionView(frame: CGRect(x: 0, y: 0, width: ScreenWidth, height: 100), collectionViewLayout: layout)
        collection?.register(SkuCollectionCell.self, forCellWithReuseIdentifier: SkuCollectionCell.identifier)
        collection?.register(SkuHeaderCell.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: SkuHeaderCell.identifier)
        collection?.backgroundColor = .white
        
//        self.collection?.addObserver(self, forKeyPath: #keyPath(UICollectionView.collectionViewLayout.collectionViewContentSize), options: [.new], context: nil)
//        self.collection?.observe(\UICollectionView.collectionViewLayout.collectionViewContentSize, changeHandler: {[unowned self] (collection, newValue) in
//            if(collection.superview == nil){
//
//            }
//
//        })
        collection?.dataSource = self
        collection?.delegate = self
        self.addSubview(collection!)
//        print(itemSize(title: "8888888888", height: 40).width)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    @objc func tapView(){
        
    }
    
//    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
//        if keyPath == #keyPath(UICollectionView.contentOffset) {
//            if let co = self.collection {
//                print(co.collectionViewLayout.collectionViewContentSize.height)
//            }
//        }
//    }
    
    deinit {
//        self.collection?.removeObserver(self, forKeyPath: #keyPath(UICollectionView.contentSize))
    }
    
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let view = super.hitTest(point, with: event)
//        print(object_getClass(view))
         if let bool = (view?.isKind(of: UICollectionView.self)) {
                   if bool {
                       return self
                   }
               }
        return view
    }
    
}

extension SkuCell: UICollectionViewDelegateFlowLayout {
    
    func itemSize(title: String, height: CGFloat) ->CGSize{
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: 0, height: height))
        label.numberOfLines = 0
        label.font = UIFont.systemFont(ofSize: 14)
        label.text = title
        label.textAlignment = .center
        label.sizeToFit()
        return CGSize(width: label.bounds.size.width + 10, height: height)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 10, left: 5, bottom: 10, right: 5)
    }
    //设定指定区内Cell的最小间距,也可以直接设置UICollectionViewFlowLayout的minimumInteritemSpacing属性
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat{
        return 10
    }
    //设定指定区内Cell的最小行距,也可以直接设置UICollectionViewFlowLayout的minimumLineSpacing属性
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 10
    }
    //设定指定Cell的尺寸
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        if let tmpModel = model?.specList?[indexPath.section]{
            if let title = tmpModel.list?[indexPath.item] {
                let size = itemSize(title: title, height: 40)
                if(size.width < 80){
                    return CGSize(width: 80, height: 40)
                }else{
                    return size
                }
            }
        }
        return CGSize(width: 80, height: 40)
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        //获取顶点
        let title = model!.specList![indexPath.section].list![indexPath.item]
        let point = model!.skuTopDict[title] as! CGPoint
        // 选中的顶点不在可选顶点中,不可选
        if(!set.isEmpty && !set.contains(title)){
            //不可选
            return
        }
        //判断是否有相同类目的顶点
        if(hasSameTop(point)){
            //有相同类目的顶点
            deleteSameTop(point)
        }else{
            // 没有相同类目的顶点直接加入集合
            alreadys.insert(point)
        }
        
        //迭代所有的条件, 同时满足alreadys里面的顶点的交集
        set.removeAll()
        for (index, arr) in model!.skuConditionList.enumerated() {
            var b = true
            var tmpSet:Set<String> = []
            for already in alreadys {
                if arr[Int(already.y)] != Int(already.x){
                    b = false
                }
            }
            if(b){
                
                tmpSet = Set<String>(model!.specCombinationList![index].specs!)
                set = set.union(tmpSet)
            }
        }
        
        if(alreadys.count == 1){
            let point =  alreadys.first!
            for e in model!.specList![Int(point.y)].list!{
                set.insert(e)
            }
        }
        collection?.reloadData()
        if(alreadys.count == model!.skuConditionList.count){
            //选择完成
            for (index, arr) in model!.skuConditionList.enumerated() {
                var flag = true
                for already in alreadys {
                    if arr[Int(already.y)] != Int(already.x) {
                        flag = false
                    }
                }
                if flag {
                    let count = model!.specCombinationList![index].count
                    let id = model!.specCombinationList![index].id
                    print("选择完成: id:\(id)  剩余数量: + \(count)")
                }
            }
        }
    }
    
    //是否是同一个类目的顶点
    func hasSameTop(_ point: CGPoint) -> Bool{
        var flag = false
        for obj in alreadys {
            if point.y == obj.y{
                flag = true
            }
        }
        return flag
    }
    
    //处理相同顶点
    func deleteSameTop(_ point: CGPoint){
        if alreadys.contains(point){
            //选择的同一个顶点
            alreadys.remove(point)
        }else{
            //选择同一个类目不同的顶点, 先清空集合内同一个类目的顶点, 在加入选中的顶点
            for  (_, already) in  alreadys.enumerated() {
                if(already.y == point.y){
                    alreadys.remove(already)
                }
            }
            alreadys.insert(point)
        }
    }
    
}

extension SkuCell: UICollectionViewDataSource {
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return model?.specList?.count ?? 0
        
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if let tmpModel = model?.specList?[section]{
            if let list = tmpModel.list {
                return list.count
            }
        }
    
        return 0
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell: SkuCollectionCell = collectionView.dequeueReusableCell(withReuseIdentifier: SkuCollectionCell.identifier, for: indexPath) as! SkuCollectionCell
        if let tmpModel = model {
            if let topModel = tmpModel.specList?[indexPath.section]{
                if let list = topModel.list {
                    cell.setName(title: list[indexPath.item])
                    if(alreadys.contains(model!.skuTopDict[list[indexPath.item]] as! CGPoint)){
                        cell.setStatus(status: .selected)
                    }else{
                        if(set.isEmpty || set.contains(list[indexPath.item])){
                            cell.setStatus(status: .normal)
                        }else{
                            cell.setStatus(status: .disable)
                        }
                    }
                }
            }
        }
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        let header: SkuHeaderCell = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: SkuHeaderCell.identifier, for: indexPath) as! SkuHeaderCell
        
        if let tmpModel = model {
            if let topModel = tmpModel.specList?[indexPath.section]{
                if let title = topModel.title {
                    header.label?.text = title
                }
            }
        }
        return header
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        return CGSize(width: ScreenWidth, height: 30)
    }
}

class SkuHeaderCell: UICollectionReusableView {
    
    static let identifier = "SkuHeaderCell"
    var label: UILabel?
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        label = UILabel(frame: CGRect(x: 10, y: 0, width: frame.width - 15, height: frame.height))
        label?.textColor = .black
        label?.font = UIFont.systemFont(ofSize: 13)
        addSubview(label!)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

enum SkuCellStatus {
    case normal
    case disable
    case selected
}


class SkuCollectionCell: UICollectionViewCell {
    
    static let identifier = "SkuCollectionCell"
    
    var name: UILabel?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        name = UILabel(frame: CGRect(x: 0, y: 0, width: frame.width, height: frame.height))
        name?.numberOfLines = 0
        name?.font = UIFont.systemFont(ofSize: 14)
        name?.textColor = .black
        name?.textAlignment = .center
        contentView.addSubview(name!)
        setStatus(status: .normal)
    }
    
    func setStatus(status: SkuCellStatus){
        switch status {
        case .disable:
            name?.textColor = .gray
            name?.setBorderStyle(width: 1, color: .gray)
        case .selected:
            name?.textColor = .red
            name?.setBorderStyle(width: 1, color: .red)
        default:
            name?.textColor = .black
            name?.setBorderStyle(width: 1, color: .black)
        }
    }
    
    func setName(title: String){
        self.name?.text = title
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let view = super.hitTest(point, with: event)
         if let bool = (view?.isKind(of: UICollectionView.self)) {
                   if bool {
                       return self
                   }
               }
        return view
    }
}

相关文章

  • Swift Sku商品筛选实现

    git链接 效果图image.png 先看下json数据 转model用的swiftPackage 集成Handy...

  • 商城中开发商品sku及其他汇总

    1: sku商品,点击商品进去并不是商品的ID而是sku的id 基本表, 规格表 , 规格属性表 商品sku表 ...

  • 4.pandas 数据筛选 与 逻辑赋值

    0 实验数据 仓库商品信息全表(表名=sku_all) 1 数据筛选(等价于SQL中的where语句)

  • JavaScript 实现笛卡尔乘积

    题目:JavaScript 实现笛卡尔乘积,一般用于商品 sku 属性配置,例如输入 ['1', '2'], ['...

  • B2B产品触发器

    写入sku表触发 更新sku表触发 批量导入商品属性表 根据通用名归档商品分类

  • 电商之路

    从海量铺货开始 8000SKU 100%利润 筛选出优质类目再铺5000SKU 150%利润 筛选出优质产品 ...

  • iOS上商品SKU实现方法

    前言 前段时间刚接到产品需求,需要改版商城,其中就涉及到类似天猫京东那样的商品SKU,而且需要兼容多种规格,还需要...

  • EXCEL 实战笔记

    去除重复项+sumif函数筛选 最近在做一份报表。首先这份表格是这样的: 电商形式的订单,有实付金额,商品sku,...

  • 在线商城项目12-商品列表页价格筛选实现

    简介 本篇主要目的如下: 实现商品列表页的后端价格筛选逻辑 前后端联调价格筛选逻辑 1. 实现商品列表页的后端价格...

  • 算法实际应用集(上)

    使用笛卡尔算法进行sku组合 需求 对商品规格进行排列组合,电商的sku商品组合 功能截图,对商品规格进行组合排列...

网友评论

      本文标题:Swift Sku商品筛选实现

      本文链接:https://www.haomeiwen.com/subject/ouxwrktx.html