Skip to content

ChrishonWyllie/ModularSidebarView

Repository files navigation

ModularSidebarView

CI Status Version License Platform

ModularSidebarView is a customizable menu for displaying options on the side of the screen for iOS. It is meant to act as a substitute to the usual UINavigation bar items and tool bar items.



Usage

Displaying the SidebarView

First, initialize the SideBarView

private lazy var sidebarView: SidebarView = {
    let sbv = SidebarView(delegate: self)
    // Make desired configurations
    return sbv
}()


Then create some sort of UIButton, UIBarButtonItem or any view with userInteraction enabled. Create the selector and function however you choose.

// It is NOT required to a use a UIBarButtonItem to display the SidebarView. 
// Provide your own implementation for triggering the SidebarView to show.
private lazy var sidebarButton: UIBarButtonItem = {
    let btn = UIBarButtonItem(title: "Sidebar", 
                              style: .done, 
                              target: self, 
                              action: #selector(openSidebarView(_:)))
    return btn
}()

// Here, we call the "showSidebarView()" function to display the SidebarView
@objc private func openSidebarView(_ sender: Any) {
    sidebarView.show()
}

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    // ... Other UI setup

    // Place the sidebarButton in the navigationBar if desired.
    self.navigationItem.leftBarButtonItem = sidebarButton
}

Dismissing the SidebarView

private func dismissSidebarView() {
    sidebarView.dismiss()
}
  • Simply tap the background view

  • Or pressing one of the options in the SidebarView will also dismiss on selection if dismissesOnSelection set to TRUE

Adding items to the SidebarView

The SidebarView uses a View-Model approach to laying out items.

You may subclass the default provided classes or conform to the underlying protocol for more customization

Step 1: Creating View-Models

Subclassing the SidebarViewCellModel, SidebarViewReusableViewSectionModel and SidebarViewReusableViewModel

// SidebarViewCellModel is the View-Model that represents a SidebarView cell
// CustomSidebarCellModel is a custom subclass. You may provide your own subclass
class CustomSidebarCellModel: SidebarViewCellModel {
    
    var image: UIImage?
    var title: String
    
    init(image: UIImage?, title: String) {
        self.image = image
        self.title = title
        
        // This is very imporant. Provide the cell class you wish for this model to display
        // This must be a UICollectionViewCell
        super.init(cellClass: CustomSidebarCell.self)
    }
}
// At heart, SidebarView is a UICollectionView.
// As such, may render both a header and footer supplementaryView for each section.
// The SidebarViewReusableViewSectionModel provides a container for both the header and footer in each section.

SidebarViewReusableViewSectionModel(headerViewModel: SidebarViewReusableViewModelProtocol?, footerViewModel: SidebarViewReusableViewModelProtocol?)

// You create your own header and footer supplementary view-models that conform to SidebarViewReusableViewModelProtocol or subclass the default SidebarViewReusableViewModel:

class CustomHeaderModel: SidebarViewReusableViewModel {
    
    init() {
        super.init(reusableViewClass: CustomSidebarSectionHeader.self, elementType: .header)
    }
}

class CustomFooterModel: SidebarViewReusableViewModel {
    
    init() {
        super.init(reusableViewClass: CustomSidebarSectionHeader.self, elementType: .footer)
    }
}

Step 2: Creating Views (Cells and ReusableViews)

Subclassing the SidebarViewCell and SidebarReusableView

class CustomSidebarCell: SidebarViewCell {

    // Important to override this function to configure the cells as desired
    override func configure(with item: SidebarViewCellModelProtocol, at indexPath: IndexPath) {
        super.configure(with: item, at: indexPath)
        guard let customViewModel = item as? CustomSidebarCellModel else { 
            return
        }
        
        // configure the cell with your custom View-Model data
        self.imageView.image = customViewModel.image
        
        self.titleLabel.text = customViewModel.title
    }
}

class CustomSidebarSectionHeader: SidebarReusableView {

    // Important to override this function to configure the view as desired
    override func configure(with item: SidebarViewReusableViewModelProtocol, at indexPath: IndexPath) {
        super.configure(with: item, at: indexPath)
        guard let customViewModel = item as? CustomHeaderModel else {
            return
        }
        
        // configure the cell with your custom header View-Model data
        
    }
}

Step 3: Inserting the View-Models into the SidebarView

Use these two functions to insert Cell and ReusableView View-Models at desired indexPaths

func insertSidebarView(models: [SidebarViewCellModelProtocol], atIndexPaths indexPaths: [IndexPath])

func insertReusableView(reusableSectionModels: [SidebarViewReusableViewSectionProtocol], atIndices indices: [Int])

Example

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    
    setupSidebarViewItems()
    
}

private func setupSidebarViewItems() {

    // Create Cell View-Models

    let sectionOneIndexPaths: [IndexPath = // ... Construct array of IndexPaths for section 0
    let sectionOneItems: [SidebarViewCellModelProtocol] = // ... Construct array of View-Models for section 0
    
    let sectionTwoIndexPaths: [IndexPath = // ... Construct array of IndexPaths for section 1
    let sectionTwoItems: [SidebarViewCellModelProtocol] = // ... Construct array of View-Models for section 1
    
    let completeListOfItems: [SidebarViewCellModelProtocol] = (sectionOneItems + sectionTwoItems)
    let completeListOfIndexPaths = (sectionOneIndexPaths + sectionTwoIndexPaths)
    
    sidebarView.insertSidebarView(models: completeListOfItems, atIndexPaths: completeListOfIndexPaths)
    
    
    
    
    // Create ReusableView View-Models
    
    let reusableSectionItems: [SidebarViewReusableViewSectionModel] = // ... Construct array of ReusableView Section View-Models
    let sectionIndices: [Int] = // ... Construct of section indices (positive integers)
    
    sidebarView.insertReusableView(reusableSectionModels: reusableSectionItems, atIndices: sectionIndices)
    
}

Extending the SidebarViewDelegate

You may extend the class:

class ViewController: SidebarViewDelegate {

    func sidebarView(_ sidebarView: SidebarView, didSelectItemAt indexPath: IndexPath) {
        
        // Provide custom action/functionality when tapping a SidebarView cell at each indexPath
        
    }
    
    func sidebarView(_ sidebarView: SidebarView, heightForHeaderIn section: Int) -> CGFloat {
        
        // Provide height for supplementary header reusableView at each section
        
    }
    
    func sidebarView(_ sidebarView: SidebarView, heightForFooterIn section: Int) -> CGFloat {
        
        // Provide height for supplementary footer reusableView at each section
        
    }
    
    func sidebarView(_ view: SidebarView, heightForItemAt indexPath: IndexPath) -> CGFloat {
        
        // Provide heights for items at each IndexPath
        
    }
}

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

Requirements

Installation

ModularSidebarView is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'ModularSidebarView'

Author

ChrishonWyllie, [email protected]

License

ModularSidebarView is available under the MIT license. See the LICENSE file for more info.