UIButton을 꾹 눌렀을 때 나타나는 메뉴들에 대해서 알아보자.
UIButton의 menu 프로퍼티는 iOS14에서 새로 추가된 속성으로 iOS13에서의 모습과 차이를 보인다.
예를 들어 iOS13의 safari에 들어갔을 때 하단에 tool bar가 보인다. 이 toolbar에 위치한 버튼을 길게 누르면 아래와 같이 action sheet가 보이는 것을 확인할 수 있다.
똑같은 safari에 대해서 iOS14에서 해당 작업을 수행하면
-> 이런 것처럼 action sheet가 뜨는 것이 아니라 menu가 보이는 것을 확인할 수 있다.
=> UIButton의 클래스를 확인해보자
앞서 말한 것처럼 iOS14부터 적용할 수 있으며 UIMenu 타입이고 기본값은 nil임을 확인할 수 있다.
UIMenu 프로퍼티 자체는 iOS13에서 추가된 항목이다.
iOS13에서 이런 메뉴가 사용된 예시는 safari에서 3d touch를 했을 때 볼 수 있다.
safari 창의 아래로 보이는 메뉴들은 context menu라고 할 수 있는데 이것을 UIMenu를 통해 만들 수 있다.
여기서 UIMenuElement를 상속받고 있는 클래스들은
이렇게 있고 위의 것들 중에서 UIAction을 넣어보도록 하자.
let button = UIButton(type: .system)
button.menu = UIMenu(title: "title입니다",
image: UIImage(systemName: "heart.fill"),
identifier: nil,
options: .displayInline,
children: [])
이렇게 코드를 작성하면 children에 빈 배열을 적용했기 때문에 버튼을 눌러도 아무런 메뉴가 뜨지 않는다.
위에서 children에 UIMenuElement 타입이 들어갈 수 있다고 말했던 것처럼 여기에 UIAction을 적용해보면,
let button = UIButton(type: .system)
let favorite = UIAction(title: "즐겨찾기",
image: UIImage(systemName: "heart"),
handler: { _ in print("즐겨찾기") })
button.menu = UIMenu(title: "title입니다",
identifier: nil,
options: .displayInline,
children: [favorite])
버튼을 길게 눌렀을 때,
이렇게 나타나는 것을 볼 수 있다.
좀 더 다양한 메뉴 옵션을 주고 싶다면? UIAction을 수정하면 된다!!
UIAction에는 attribute를 줄 수 있다.
이 중에서 destructive를 사용하면
let favorite = UIAction(title: "즐겨찾기",
image: UIImage(systemName: "heart"),
handler: { _ in print("즐겨찾기") })
let cancel = UIAction(title: "취소",
attributes: .destructive,
handler: { _ in print("취소") })
button.menu = UIMenu(title: "title입니다",
image: UIImage(systemName: "heart.fill"),
identifier: nil,
options: .displayInline,
children: [favorite, cancel])
이렇게 버튼의 메뉴에 favorite과 cancel 옵션을 넣을 수 있고 children 배열 순서대로 menu에 들어가게 된다.
UIMenu의 파라미터 중 children은 [UIMenuElement]타입을 파라미터로 받고 있다. 이 중 UIMenu도 해당 타입에 속해 있다는 것을 볼 수 있다.
UIMenu안에 또 다른 UIMenu를 넣을 수 있다 ??
= YES
let favorite = UIAction(title: "즐겨찾기", image: UIImage(systemName: "heart"), handler: { _ in print("즐겨찾기") })
let cancel = UIAction(title: "취소", attributes: .destructive, handler: { _ in print("취소") })
let menuAction = UIAction(title: "메뉴 안 메뉴", image: UIImage(systemName: "house"), handler: { _ in print("메뉴 안의 메뉴") })
let menu = UIMenu(title: "메뉴 title", children: [menuAction])
button.menu = UIMenu(title: "title입니다",
image: UIImage(systemName: "heart.fill"),
identifier: nil,
options: .displayInline,
children: [favorite, menu, cancel])
이렇게 작성하면 버튼을 길게 눌렀을 때 아래와 같이 총 3개의 옵션이 보이고
이 중에서 메뉴 title을 다시 길게 누르면 해당 액션이 보여지는 것을 확인할 수 있다.
위에서 말한 것처럼 UIMenu에 적용할 수 있는 옵션은 크게 두가지로 displayInline, destructive로 구분할 수 있다.
만약 어떠한 옵션도 주지 않는다면, 지금까지 구현한 것처럼 메뉴 안 메뉴를 만들었을 때 부모-자식간의 계층 관계로 안으로 들어가야 해당 메뉴가 보이는 것을 확인할 수 있다.
이 때, displayInline 옵션을 사용하게 된다면?
계층적 관계로 보여주는 것이 아니라, 인라인으로 수평적 관계로 보여주겠다는 의미이다.
메뉴의 모양이 destructive로 보여야 하는지 설정하는 것
옵션에 해당 속성 값을 적용하게 되면
이런 식으로 보이는 것을 확인할 수 있다.
UIAction에서 state 속성 값을 줄 수 있었고 이에 따라서 UIMenu에서 보여지는 화면이 달라짐을 확인할 수 있다.
state를 on으로 설정하게 되면
UIMenu를 선택했을 때 체크표시가 나타나는 것을 확인할 수 있다.
(여러개를 선택할 수도 있고 pull up button의 경우 하나로 제한할 수 있다.)
해당 값을 true로 설정하게 되면 버튼을 길게누르지 않고 tap했을 때도 바로 메뉴가 나타나게 된다.