Как программно представить меню при однократном касании строки представления таблицы, как в приложении «Календарь» iOS 16?

В приложении «Календарь» iOS 16 появился новый стиль раскрывающегося меню для таких параметров, как «повторить», при нажатии на любое место строки появлялось меню. И в правой части ячейки представления таблицы есть значок шеврона вверх и шеврона вниз.

Как это сделать в iOS 16? Контекстное меню вызывается долгим нажатием, но этот новый стиль — одним нажатием.

4 ответа

Вы можете добавить UIButton в TableViewCell и заполнить все пространство. Используйте свойство меню UIButton для достижения эффекта системного календаря.

      button.menu = menu

Вы можете проверить код, который я написал, надеюсь, он вам поможет. https://github.com/zhi6w/TableViewCellWithMenuButton

Вопрос в том, какое всплывающее меню используется в приложении «Календарь». Кнопка представляет собой всплывающую кнопку . В UIKit это реализовано с помощьюUIButton. Вам нужно настроить кнопкуmenuи установитеshowsMenuAsPrimaryActionиchangesSelectionAsPrimaryActionсвойства дляtrue.

Вот пример кода для создания такой кнопки и применения ее к ячейке таблицы:

      override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   let cell = ... // Create and setup your cell as needed

   // Create the popup button
   let cfg = UIButton.Configuration.plain()
   let button = UIButton(configuration: cfg)
   // Replace the following with your own code to support your own menu.
   // The important part here is that zero or one actions is set with a
   // state of on and the rest are set to off. This defines the initially
   // selected value, if any. The state of the actions are updated as the
   // user makes a selection.
   button.menu = UIMenu(options: [.singleSelection], children: [
        UIAction(title: "Option 1", state: self.option == .option1 ? .on : .off, handler: { [weak self] action in
            self?.option = .option1
        }),
        UIAction(title: "Option 2", state: self.option == .option2 ? .on : .off, handler: { [weak self] action in
            self?.option = .option2
        }),
    ])
    // After setting the menu, the following two properties signify that
    // the button is to look and act like a popup button. This includes
    // the up/down arrows on the right side of the button which is shown
    // by default for a popup button.
    button.showsMenuAsPrimaryAction = true
    button.changesSelectionAsPrimaryAction = true

    // This puts the button on the right end of the cell.
    cell.accessoryView = button

    return cell
}

Код меню, показанный выше, предполагает наличие некоторыхenumпредставляющие различные выбираемые опции и что существуетoptionсвойство для отслеживания выбранной опции. Существует много возможных способов и вариантов использования меню, поэтому я оставляю это на усмотрение вашего приложения. Важная часть этого ответа показывает базовую настройку, необходимую для репликации всплывающей кнопки, используемой в приложении «Календарь».

Начиная с iOS 16, вы можете сделать это, хотя и с помощью горизонтально расположенного меню редактирования или, альтернативно, с помощью жеста длительного нажатия, чего пользователь не может ожидать.

В первом случае вы предоставляете UIMenu методу делегата меню редактирования, а во втором вы используете меню делегата контекстного меню. Для горизонтального меню, чем короче имена меню, тем лучше, особенно если у вас более 2 или 3 элементов, чтобы пользователю не приходилось щелкать правый треугольник в конце, чтобы отобразить больше элементов.

Методы делегата соответственно возвращают UIMenu, который вы можете создать прямо в методе делегата, или вы можете настроить его для повторного использования вызванного упоминания, возможно, с помощью всплывающего окна UIButton() внутри ячейки tableView, так вы поправитесь. покрытие на случай, если пользователь не нажмет саму кнопку.


Способ 1: Пользовательское меню редактирования (например, существующее горизонтальное меню):
      var editMenuInteraction : UIEditMenuInteraction! = nil

override func viewDidLoad() {
    editMenuInteraction = UIEditMenuInteraction(delegate: self)
}

extension MyViewController: UIEditMenuInteractionDelegate {
    
    func editMenuInteraction(_ interaction: UIEditMenuInteraction, menuFor configuration: UIEditMenuConfiguration, suggestedActions: [UIMenuElement]) -> UIMenu? {


        let myAction = UIAction(title: "Action1", image: UIImage(systemName: "bell"), identifier: nil) { action in
            print("MyAction tapped")
        }

        let anotherAction = UIAction(title: "Action2", image: UIImage(systemName: "person"), identifier: nil) { action in
            print("AnotherAction tapped")
        }

        .
        .
        .

        return UIMenu(title: "", children: [myAction, anotherAction])
    }
}

extension MyViewController : UITableViewDelegate {
   func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            cell.addInteraction(editMenuInteraction)
   }

   func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if let interaction = editMenuInteraction {
           let rect = tableView.rectForRow(at: indexPath)
           let absoluteRect = tableView.convert(rect, to: tableView.superview)
           let configuration = UIEditMenuConfiguration(identifier: nil, sourcePoint: absoluteRect.origin)
           interaction.presentEditMenu(with: configuration)
        }
    }
}

Способ 2: контекстное меню (например, долгое нажатие на ячейку)
      // In your cellForRowAt tableView delegate method:

let interaction = UIContextMenuInteraction(delegate: self)
cell?.addInteraction(interaction)


extension MyViewController: UIContextMenuInteractionDelegate {
    func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
        return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { suggestedActions in
        
            let myAction = UIAction(title: "MyAction", image: UIImage(systemName: "bell"), identifier: nil) { action in
                print("MyAction tapped")
            }

            let anotherAction = UIAction(title: "AnotherAction", image: UIImage(systemName: "person"), identifier: nil) { action in
                print("AnotherAction tapped")
            }
         
            .
            .
            .

            return UIMenu(title: "", children: [myAction, anotherAction])
        }
    }
}

Вы можете добавить меню отображения UIButton в contentView tableViewCell.

      class TestCell: UITableViewCell {
  lazy var menuButton: UIButton = {
    let button = UIButton()
    button.showsMenuAsPrimaryAction = true
    // custom your menu
    button.menu = UIMenu(title: "menus", children: [])
    return button
  }()

  override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: .default, reuseIdentifier: reuseIdentifier)
    contentView.addSubview(menuButton)
  }
}
Другие вопросы по тегам