Side Bar

  • A FUISideBar<Data> is a generic view that displaying data in an expandable list within a side bar.

    Usage

    Initialization:

    Implement a data model for the expandable list that will be displayed in side bar.

    struct BarItem: Identifiable, Hashable {
        let id = UUID()
        let title: String
        var icon: UIImage?
        var filledIcon: UIImage?
        var subtitle: String?
        var status: UIImage?
        let children: [BarItem]?
    
        func hash(into hasher: inout Hasher) {
            hasher.combine(self.id)
            hasher.combine(self.title)
        }
    
        static func == (lhs: BarItem, rhs: BarItem) -> Bool {
            lhs.id == rhs.id
        }
    
        init(title: String, icon: UIImage? = nil, filledIcon: UIImage? = nil, subtitle: String? = nil, status: UIImage? = nil, children: [BarItem]? = nil) {
            self.title = title
            self.icon = icon
            self.filledIcon = filledIcon
            self.subtitle = subtitle
            self.status = status
            self.children = children
        }
    }
    
    let items: [BarItem] = [
        BarItem(title: "Root Item 0.1", icon: UIImage(systemName: "cloud.snow"), subtitle: "9,999+", status: UIImage(systemName: "clock"), children: nil),
        BarItem(title: "Root Item 0.4", icon: UIImage(systemName: "cloud.snow"), children: nil),
        BarItem(title: "Group 1", children: [
            BarItem(title: "Child Item 1.1", icon: UIImage(systemName: "square.and.pencil"), subtitle: "66", status: UIImage(systemName: "circle"), children: nil),
            BarItem(title: "Child Item 1.2", icon: UIImage(systemName: "square.and.pencil"), status: UIImage(systemName: "circle"), children: nil)
        ]),
        BarItem(title: "Group 2", children: [
            BarItem(title: "Child Item 2.1", icon: UIImage(systemName: "folder"), subtitle: "5", status: UIImage(systemName: "mail"), children: nil)
        ])
    ]
    

    Initialize an FUISideBar with the data model, a binding of row content view, and the initially selected item. Please note, with this initializer, the sidebar does not support editing.

    let sidebar = FUISideBar(data: items,
                             children: \.children,
                             rowContent: { item in
                               FUISideBarListItem(icon: item.icon, filledIcon: item.filledIcon, title: item.title, subtitle: item.subtitle, accessoryIcon: item.status)
                             },
                             selectedItem: items.first)
    

    Or, Initialize an FUISideBar with the data model, you will need to provide an array of FUISideBarItemModel, a binding of row content view, and the initially selected item. With this initializer, the sidebar support editing.

    struct BarItem: FUISideBarItemModel {
        var id: UUID = UUID()
        var title: String
        var icon: UIImage?
        var filledIcon: UIImage?
        var subtitle: String?
        var status: UIImage?
        var isInvisible: Bool
        var children: [any FUISideBarItemModel]?
    
        static func == (lhs: EditableBarItem, rhs: EditableBarItem) -> Bool {
            lhs.id == rhs.id
        }
    
        func hash(into hasher: inout Hasher) {
            hasher.combine(self.id)
            hasher.combine(self.title)
        }
    
        init(title: String, icon: UIImage? = nil, filledIcon: UIImage? = nil, subtitle: String? = nil, status: UIImage? = nil, children: [EditableBarItem]? = nil, isInvisible: Bool = false) {
            self.title = title
            self.icon = icon
            self.filledIcon = filledIcon
            self.subtitle = subtitle
            self.status = status
            self.children = children
            self.isInvisible = isInvisible
        }
    }
    
    let items: [BarItem] = [
        BarItem(title: "Root Item 0.1", icon: UIImage(systemName: "square.dashed), filledIcon: UIImage(systemName: "square.dashed.inset.filled"), subtitle: "9,999+", status: UIImage(systemName: "clock"), children: nil),
        BarItem(title: "Root Item 0.4", icon: UIImage(systemName: "cloud.snow"), children: nil),
        BarItem(title: "Group 1", children: [
            BarItem(title: "Child Item 1.1", icon: UIImage(systemName: "square.and.pencil"), subtitle: "66", status: UIImage(systemName: "circle"), children: nil),
            BarItem(title: "Child Item 1.2", icon: UIImage(systemName: "square.and.pencil"), status: UIImage(systemName: "circle"), children: nil)
        ]),
        BarItem(title: "Group 2", children: [
            BarItem(title: "Child Item 2.1", icon: UIImage(systemName: "folder"), subtitle: "5", status: UIImage(systemName: "mail"), children: nil)
        ])
    ]
    
    let sidebar = FUISideBar(data: items,
                             rowContent: { item in
                               FUISideBarListItem(icon: item.icon, filledIcon: item.filledIcon, title: item.title, subtitle: item.subtitle, accessoryIcon: item.status)
                             },
                             selectedItem: items.first)
    

    Selection Handler:

    A selectionDidChange closure may be used to custom the handling of an selected data element.

    sidebar.selectionDidChange = { [weak self] item in
        self?.presentationDidChange?(item)
    }
    

    Handle Editing:

    An ‘isEditing’ property of FUISideBar<Data> indicates whether the sidebar is in editing mode or not. Please note, it is essential to use the corresponding Initialize method which permits the editing of the sidebar. Additionally, an ‘Edit’ button can be added to the navigation bar to allow the sidebar to transition from editing mode to the view model.

    self.navigationItem.rightBarButtonItem = self.editButtonItem
    
    override func setEditing(_ editing: Bool, animated: Bool) {
        super.setEditing(editing, animated: animated)
        self.sideBar?.isEditing = editing
        configNavBarStyle()
        configureSearchBarOnNavBar(true)
    }
    

    Monitor changed data:

    A dataChange closure may be used to receive notifications when the sidebar transitions from its editing mode to the view model if the sidebar item was reordered or visible was changed. This arrangement facilitates effective monitoring and management of changes. This should be used exclusively when the sidebar supports editing and you are particularly concerned about the modified data.

    sideBar?.dataChange = { (items: [BarItem]) in
        // Check data changes
    }
    

    A ‘queryString’ property of FUISideBar<Data> is used to trigger the searching on SideBar. You can add the FUISearchController as navigation item for searching and get the query string from UISearchResultsUpdating.

    let fuiSearchController = addResultsController ? FUISearchController(searchResultsController: SearchResultsViewController()) : FUISearchController(searchResultsController: nil)
    fuiSearchController.searchResultsUpdater = self
    fuiSearchController.hidesNavigationBarDuringPresentation = hidesNavBar
    fuiSearchController.setsNavigationBarColor = true
    fuiSearchController.setsStatusBarStyle = true
    fuiSearchController.searchBar.placeholderText = "Search"
    
    fuiSearchController.searchBar.delegate = self
    self.searchController = fuiSearchController
    
    navigationItem.searchController = searchController
    navigationItem.hidesSearchBarWhenScrolling = true // false
    
    func updateSearchResults(for searchController: UISearchController) {
       self.sideBar?.queryString = searchController.searchBar.text
    }
    
    See more

    Declaration

    Swift

    @available(iOS 14, *)
    @MainActor
    open class FUISideBar<Data>: UIView where Data: RandomAccessCollection,
                                              Data.Element: Identifiable & Hashable
  • A view for each list item in the FUISideBar

    See more