Implement Checkmark on Dropdown menu like Files app on Swift.

Function realization:

Section1: single option. Section2: multiple options

Screenshot:

Example:

The import UIKit import SwiftExpand @ the available (iOS 14.0. *) class MenuActionChooseOneController: UIViewController {var menuData: [(String, [(String, UIImage?)])] {return [("Menu", [(title: "select ", image: Uiimage.checkmark_circle), (title: "new folder ", image: uiimage.folder_badge_plus), (title:" Scan document ", image: Uiimage.doc_text_viewfinder), (title: "Connect to server ", image: Rectangle_connected_to_line_below),]), ("Menu1", [(title: "icon ", image: uiimage.square_grid_2x2), (title: rectangle_connected_to_line_below), ("Menu1", [(title:" icon ", image: uiimage.square_grid_2x2), (title: "List ", image: uiimage.list_bullet),]), ("Menu2", [(title:" name ", image: uiimage.chevron_up), (title: "date ", image: Uiimage.chevron_down), (title: "size ", image: uiimage.chevron_left), (title:" type ", image: uiimage.chevron_right), (title: "type ", image: uiimage.chevron_right), (title: ]),]} lazy var menuBtn: UIButton = {let sender = UIButton(type: .custom) sender.setTitle("menu", for: .normal); sender.showsMenuAsPrimaryAction = true sender.menu = UIMenu.map(data: menuData, handler: {action in switch action.title {case "icon "," list ": Action. handleStateChange(Sender, Section: 1, isSingleChoose: true) { DDLog(sender.checkRow(by: 1)) // DDLog(sender.checkActions(by: 1)) let tmp = sender.checkActions(by: 1) DDLog(tmp? .map({$0.title}))} case "name "," date ", "size "," type ", "label ": Action.HandleStatechange (Sender, Section: 2, isSingleChoose: false) { DDLog(sender.checkRow(by: 2)) // DDLog(sender.checkActions(by: 2)) let tmp = sender.checkActions(by: 2) DDLog(tmp? .map({ $0.title })) } default: DDLog(action.title) } }) return sender }() var isListMode: Bool{ guard let row = menuBtn.checkRow(by: 1) else { return true } return row == 1 // guard let menu1 = self.menuBtn.menu? .children[1] as? UIMenu, // let value = menu1.children[0].value(forKey: kActionState) as? Int // else { return true } // return value == 1 } // MARK: -lifecycle override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. edgesForExtendedLayout = [] view.backgroundColor = .white title = "MenuActionChooseOne" navigationItem.rightBarButtonItem = UIBarButtonItem(customView: menuBtn) } // MARK: -funtions }Copy the code

UIMenu+Helper


/// UIAction: "_state"
public let kActionState = "_state"

@available(iOS 14.0, *)
public extension UIMenu{

    /// Data mapping UIMenu structure
    static func map(_ title: String = "", data: [(String, [(String, UIImage?)])], handler: @escaping UIActionHandler) -> UIMenu {
        return UIMenu(title: title, children: data.map {
            UIMenu(title: $0.0, options: .displayInline, children: $0.1.map({
                    return UIAction(title: $0.0, image: $0.1, handler: handler)
                })
            )
        })
    }
}

@available(iOS 14.0, *)
public extension UIAction{
    
    /// state change(.on/.off), Support single selection/multiple selection.
    func handleStateChange(_ sender: UIButton, section: Int, isSingleChoose: Bool, handler: (()->Void)?) {
        guard let menu = sender.menu?.children[section] as? UIMenu else { return }
        if isSingleChoose == false {
            if self.state == .off {
                self.setValue(1, forKey: kActionState)
            } else if self.state == .on {
                self.setValue( 0, forKey: kActionState)
            }
        } else {
            menu.children.forEach {
                $0.setValue($0 == self ? 1 : 0, forKey: kActionState)
            }
        }
        handler?()
    }
}


@available(iOS 14.0, *)
public extension UIButton {
        
    /// Actions (Support single selection/multiple selection)
    /// - Returns: row(state is on)
    func checkActions(by section: Int) -> [UIAction]?{
        guard let sectionMenu = menu?.children[section] as? UIMenu
              else { return nil }
        
        let firters = sectionMenu.children.filter {
            guard let action = $0 as? UIAction,
                  let value = action.value(forKey: kActionState) as? Int else { return false }
            return value == 1
        }
        return firters as? [UIAction]
    }
    
    /// row(Support single selection)
    /// - Returns: row(state is on)
    func checkRow(by section: Int) -> Int?{
        guard let sectionMenu = menu?.children[section] as? UIMenu
              else { return nil }
        
        let firters = sectionMenu.children.filter {
            guard let value = $0.value(forKey: kActionState) as? Int else { return false }
            return value == 1
        }

        guard let checkAction = firters.first as? UIAction else { return nil }
        return sectionMenu.children.firstIndex(of: checkAction)
    }
    
}
Copy the code

Github