Multi level list number structure display in swift

In the process of program development, we often encounter the display of multi-level data structure, most of which are two or three levels. At this time, the basic use of UITableview can solve this kind of problem. However, sometimes if there are four levels and five levels in the future, it's not good to use ordinary methods. I've encountered many levels of data structure display problems before. Record them here

Because I don't have a large amount of data, I did it like this. As for too much data, you'd better watch and use it yourself 😂

I still use the UITableview here. The problem of display is to complete the operation on the data itself. In short, I add a tag (type: to display its structure level) to the data, and then make the corresponding display to distinguish according to the tag (type) in the cell.

Because there are many types of data displayed, in order to avoid writing the same logic code repeatedly in multiple model s, and to use it for better generality, I used the protocol mode, and my main code is below

import Foundation

///Used to construct multilevel trees
@objc protocol HZJTreeViewModelProtocol {
    ///Parent data (it does not need to be set manually, but will be modified automatically according to setChildModels)
    var fatherModel:HZJTreeViewModelProtocol? { get set }
    
    ///Contains the child data (to be assigned using the setChildModels method)
    var childModels:[HZJTreeViewModelProtocol] { get set }
}

//MARK: - added non private property
extension HZJTreeViewModelProtocol {
    ///Show current item or not
    public var isShowCurrent:Bool {
        return self._isShow
    }

    ///Open or not to show the childModels of the current item
    public var isOpen:Bool {
        return self._isOpen
    }
    
    ///Grade
    var type:Int{
        return self._type
    }
}

//MARK: - method used to modify properties
extension HZJTreeViewModelProtocol {
    @discardableResult
    public func toShowCurrent(_ show:Bool) -> HZJTreeViewModelProtocol{
        self._isShow = show
        return self
    }
    
    @discardableResult
    public func setOpenState(_ open:Bool) -> HZJTreeViewModelProtocol{
        self._isOpen = open
        return self
    }
}

//MARK: - other important methods
extension HZJTreeViewModelProtocol {
    ///Set the sub data (be careful not to refer to it circularly)
    /// - Parameter models: <#models description#>
    /// - Returns: <#description#>
    @discardableResult
    public func setChildModels(_ models:[HZJTreeViewModelProtocol]) -> [HZJTreeViewModelProtocol] {
        for item in models {
            item.fatherModel = self
            item._isShow = self.isOpen
        }
        self.childModels = models
        self.setAllChildModeType(self._type)
        return self.childModels
    }
    
    ///Modify the display status of the current childModels
    ///- Parameter open: display or not
    ///- Parameter allChild: for all child data
    public func changeChildModelShowState(_ open:Bool, allChild:Bool = false) {
        self._isOpen = open
        for item in self.childModels {
            item._isShow = open
            if allChild {
                item.changeChildModelShowState(open, allChild: allChild)
            }
        }
    }
    
    ///Get all sub data (including sub data of sub data of sub data... Recursive operation)
    /// - Returns: <#description#>
    public func getAllChildModel() -> [HZJTreeViewModelProtocol] {
        var result:[HZJTreeViewModelProtocol] = []
        for item in self.childModels {
            result.append(item)
            result.append(contentsOf: item.getAllChildModel())
        }
        return result
    }
    
    ///Get all the child data to be displayed (including the child data of the child data... Recursive operation, if the parent class is closed, it will not traverse down)
    /// - Returns: <#description#>
    public func getAllShowChildModel() -> [HZJTreeViewModelProtocol] {
        var result:[HZJTreeViewModelProtocol] = []
        for item in self.childModels {
            if item.isShowCurrent {
                result.append(item)
            }
            if item._isOpen {
                result.append(contentsOf: item.getAllShowChildModel())
            }
        }
        return result
    }
}

//MARK: - private property
private extension HZJTreeViewModelProtocol {
    ///Show current item or not
    var _isShow:Bool {
        set {
            objc_setAssociatedObject(self, AssociationKey.from(#function), newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        get{
            guard let t = objc_getAssociatedObject(self,  AssociationKey.from(#function)) as? Bool else {
                return true//Default
            }
            return t
        }
    }
    
    ///Open or not to show the childModels of the current item
    var _isOpen:Bool {
        set {
            objc_setAssociatedObject(self, AssociationKey.from(#function), newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        get{
            guard let t = objc_getAssociatedObject(self,  AssociationKey.from(#function)) as? Bool else {
                return false//Default
            }
            return t
        }
    }
    
    ///Grade
    var _type:Int {
        set {
            objc_setAssociatedObject(self, AssociationKey.from(#function), newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        get{
            guard let t = objc_getAssociatedObject(self,  AssociationKey.from(#function)) as? Int else {
                return 1//Default
            }
            return t
        }
    }
}

//MARK: - private method
private extension HZJTreeViewModelProtocol{
    
    ///Recursively set the type of child data
    func setAllChildModeType(_ currentType:Int){
        for item in self.childModels {
            let type =  currentType + 1
            item._type = type
            item.setAllChildModeType(type)
        }
    }
}


When using, it needs data to follow the protocol

///Data model, follow HZJTreeViewModelProtocol
class HZJTreeModel: HZJTreeViewModelProtocol  {
    
    var fatherModel: HZJTreeViewModelProtocol?
    
    var childModels: [HZJTreeViewModelProtocol] = []
    
    ///Custom properties
    var titleInfo: String = ""
    
    ///Custom convenience builder
    init(_ titleInfo:String) {
        self.titleInfo = titleInfo
    }
}

Finally, the cell uses the type attribute in the model to distinguish the display

Of course, there are also logic such as folding / display. I will not repeat it. Interested partners can see me This simple code In fact, it's quite simple 😁

Let's have another thought map

Tags: Mobile Attribute

Posted on Fri, 19 Jun 2020 06:00:29 -0400 by mfindlay