UIButton + UIMenu

pcsoyeon·2021년 10월 22일
1

UIButton을 꾹 눌렀을 때 나타나는 메뉴들에 대해서 알아보자.
UIButton의 menu 프로퍼티는 iOS14에서 새로 추가된 속성으로 iOS13에서의 모습과 차이를 보인다.

예를 들어 iOS13의 safari에 들어갔을 때 하단에 tool bar가 보인다. 이 toolbar에 위치한 버튼을 길게 누르면 아래와 같이 action sheet가 보이는 것을 확인할 수 있다.

똑같은 safari에 대해서 iOS14에서 해당 작업을 수행하면

-> 이런 것처럼 action sheet가 뜨는 것이 아니라 menu가 보이는 것을 확인할 수 있다.

=> UIButton의 클래스를 확인해보자

앞서 말한 것처럼 iOS14부터 적용할 수 있으며 UIMenu 타입이고 기본값은 nil임을 확인할 수 있다.

UIMenu

UIMenu 프로퍼티 자체는 iOS13에서 추가된 항목이다.
iOS13에서 이런 메뉴가 사용된 예시는 safari에서 3d touch를 했을 때 볼 수 있다.

safari 창의 아래로 보이는 메뉴들은 context menu라고 할 수 있는데 이것을 UIMenu를 통해 만들 수 있다.

UIMenu의 생성자

  • title : menu의 title
  • image : menu에 보여질 image
  • identifier : 해당 menu에 부여되는 고유한 값
  • options : menu에 대한 추가 옵션
    -> 두가지 옵션을 줄 수 있다.
    1. displayInline
    2. destructive
  • children : menu에 포함된 menu element

여기서 UIMenuElement를 상속받고 있는 클래스들은

  • UIMenu
  • UIAction
  • UICommand
  • UIKeyCommand
  • UIDeferredMenuElement

이렇게 있고 위의 것들 중에서 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])

버튼을 길게 눌렀을 때,

이렇게 나타나는 것을 볼 수 있다.

UIMenu + UIAction

좀 더 다양한 메뉴 옵션을 주고 싶다면? 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 + UIMenu

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 - Options

위에서 말한 것처럼 UIMenu에 적용할 수 있는 옵션은 크게 두가지로 displayInline, destructive로 구분할 수 있다.

displayInline

만약 어떠한 옵션도 주지 않는다면, 지금까지 구현한 것처럼 메뉴 안 메뉴를 만들었을 때 부모-자식간의 계층 관계로 안으로 들어가야 해당 메뉴가 보이는 것을 확인할 수 있다.

이 때, displayInline 옵션을 사용하게 된다면?
계층적 관계로 보여주는 것이 아니라, 인라인으로 수평적 관계로 보여주겠다는 의미이다.

destructive

메뉴의 모양이 destructive로 보여야 하는지 설정하는 것
옵션에 해당 속성 값을 적용하게 되면

이런 식으로 보이는 것을 확인할 수 있다.

Etc

state

UIAction에서 state 속성 값을 줄 수 있었고 이에 따라서 UIMenu에서 보여지는 화면이 달라짐을 확인할 수 있다.

state를 on으로 설정하게 되면
UIMenu를 선택했을 때 체크표시가 나타나는 것을 확인할 수 있다.
(여러개를 선택할 수도 있고 pull up button의 경우 하나로 제한할 수 있다.)

showsMenuAsPrimaryAction

해당 값을 true로 설정하게 되면 버튼을 길게누르지 않고 tap했을 때도 바로 메뉴가 나타나게 된다.

profile
Slowly But Surely

0개의 댓글