[iOS] Pull Down Button 과 Pop Up Button
엑코 13.1 열고 인터페이스 빌더에서 작업하고 있다가 기존에 없었던 컴포넌트들이 눈에 들어왔습니다
악! 또또 추가 된거 저만 몰랐죠~~ Meet the UIKit button system WWDC 안 본 제 업보죠 뭐 ㅎ
원래 Gray, Tinted, Filled Button 부터 뭔지 살펴보려고 했지만 이 내용은 Button Configuration in iOS 15 에 잘 나와있길래 이건 패스!
그럼 나머지 Pull Down Button 과 Pop Up Button 이 뭔지 알아보기 위해 바로 관련 HIG 문서로 가봅시다. (밑에 정리된건 생략 및 의역이 포함되어 있으니 되도록 원문 읽으시길?!)
Pull-Down Buttons
Message에서는 일반적인 편집 동작을 표시하기 위해 pull-down 버튼을 사용합니다.
👩🏻💻 오호,, 이게 풀 다운 버튼의 예시란 말이쥐,,, 메시지 선택, 핀 편집, 이름 및 사진 편집 등의 작업이 있군
pull-down 버튼을 사용하여 버튼의 동작(action)과 직접 관련된 항목을 표시합니다. 메뉴를 사용하면 인터페이스에 추가적인 버튼 없이도 사람들이 버튼의 target을 명확히 하거나 동작을 customize할 수 있습니다. 예를 들어,
- 추가(add) 버튼은 사람들이 추가할 항목을 지정(specify)할 수 있는 메뉴를 표시할 수 있습니다.
- 정렬(sort) 버튼은 메뉴를 사용하여 사람들이 정렬할 속성을 선택할 수 있도록 합니다.
- 뒤로(back) 버튼을 사용하면 사람들이 이전 위치를 여는 대신 특정 위치를 선택하여 다시 방문하게 할 수 있습니다.
👩🏻💻 아 pull down button 에 속하는 항목들은 “메시지 선택하기” 처럼 특정 동작이나 행위에 가깝다는 말인가?? “팝업 버튼과 달리 풀다운 버튼은 사람들이 선택하는 메뉴 항목(item)에 관계없이 항상 동일한 내용을 표시합니다.”라는 내용이 있던걸 봐서 맞는거 같기두?
하나의 풀다운 버튼에 view의 모든 작업(action)을 넣지 마십시오. 풀다운 버튼에 너무 많은 동작을 넣으면 집중도가 떨어지며 사람들이 무엇을 하든지 최소한 두 번 탭해야 합니다.
👩🏻💻 ㅇㅋㅇㅋ 다 때려 박지 않기~
메인 인터페이스에서 중요한 위치가 필요하지 않은 항목을 표시하려면 More pull-down 버튼을 사용하는 것이 좋습니다. More 버튼은 공간이 제한된 곳에서 다양한 항목을 제공하는 데 도움이 됩니다. 하지만 More 버튼이 현재 컨텍스트와 관련된 추가 기능을 제공한다는 것만 알고, 줄임표 문자 모양으로는 내용을 예측할 수 없기 때문에 항목을 발견하지 못할 수도 있습니다. 따라서 크기의 편의성과 검색 가능성(discoverability)에 미치는 영향을 비교하여 앱에서 적합한 균형을 찾아야합니다.
File 앱에서는 More pull-down 버튼을 사용하여 content 보기 및 정렬 옵션 외에도 폴더 추가 또는 문서 스캔과 같은 작업을 제공합니다.
👩🏻💻 이번 디자인 관련 WWDC 세션 봤을때 너네가 More 버튼 같은거는 discoverable 하지 않다면서 넣지 말랬던거 같은디?? 흠,, 공간에 제약이 있으면서 중요하지 않은 기능이면 More 버튼을 사용해서 넣어버리나보군? 너네도 어쩔 수 없었네~~
pull down 버튼 메뉴에서 관련 항목을 시각적으로 그룹화하려면 separator를 사용하십시오. 시각적 그룹을 만들면 사람들이 메뉴를 더 빨리 스캔하는 데 도움이 될 수 있습니다. 예를 들어 파일 앱의 더 보기(More) 버튼은 separator를 사용하여 내용에 영향을 미치는 작업과 보기 및 정렬 관련 항목을 구별하는 데 도움이 됩니다. 하지만 메뉴에서 세 개 이상의 그룹을 사용하면 parse 하기 어려워 질 수 있습니다.
👩🏻💻 오호,, seperator로 항목을 구분하는데 도움을 주되, 너무 많은 seperator 는 사용하지 않기!
pull down 버튼의 메뉴 item이 파괴적(destructive)일 때 사람들에게 알리고 의도(intent)를 확인하도록 요청하십시오. 해당 item은 강조를 위해 빨간색 텍스트를 사용하고, 선택을 확인(confirm)하거나 행동을 취소할 수 있는 action sheet(iOS) 또는 popover(iPadOS)를 표시합니다. action sheet는 메뉴와 다른 위치에 표시되고 의도적인 해제(deliberate dismissal)가 필요하기 때문에 사람들이 실수로 데이터를 잃지 않도록 도울 수 있습니다.
👩🏻💻 아 그지그지. 삭제같은거 잘못하면 승질나니까 진짜로 삭제할거니?? 하고 알림 한번 띄워주는게 예의지. 미리 알림 앱에서는 Delete List가 빨간색으로 표시 되고, 누르면 alert 띄워주네? 흠 근데 왜 설명에서는 action sheet 라고 했지? 액션 시트는 밑에서 띄워주는 애 아닌가???
메뉴 항목에 glyph를 포함합니다. 항목(item)의 의미를 명확히 해야 하는 경우 제목 뒤에 glyph나 이미지를 표시할 수 있습니다. 이러한 목적으로 SF 기호 를 사용하면 기호가 모든 scale에서 텍스트와 정렬된 상태를 유지하면서 친숙한 경험을 제공하는 데 도움이 될 수 있습니다.
glyph — 색깔 없이 흑백으로 만들어진 기호. 굳이 설명을 하지 않더라도 간단한 그래픽 이미지만으로 “이게 뭐하는거다”를 전달하기 굉장히 효과적인 방법
메인 메뉴를 이해하고 사용하기 쉽게 만드는 경우 하위 메뉴(submenu)를 사용하는 것이 좋습니다. 하위 메뉴는 사람들이 선택하면 자신의 메뉴를 표시하는 메뉴 항목입니다. 사람들이 하위 메뉴에서 항목을 선택하면 하위 메뉴가 닫히고 main 메뉴는 열려 있습니다. 밀접하게 관련된 항목을 하위 메뉴로 그룹화하면, 항목에 액세스하기 위해 추가적인 상호 작용이 필요하지만 기본 메뉴를 단축하고 검색하기 쉽게 만들 수 있습니다. 하위 메뉴를 사용해야 한다면, 사람들이 하위 메뉴가 무엇을 포함하고 있는지 알아보기 위해 열 필요가 없도록 해당 항목을 명확하게 식별하는 제목이나 글리프를 반드시 지정하십시오.
👩🏻💻 아아 예를 들어 미리 알림의 More Button 에서는 Sort By 라는 행위를 할 수 있는데, 이때 관련된 항목들을 (수동, 생성순, 우선 순위순, 제목순 등) 하위 메뉴로 묶으면 좋다는 말이군
Pop-Up Buttons
음악 앱에서는 플레이 리스트를 정렬 방법을 선택하는데 팝업 버튼을 사용합니다.
👩🏻💻 오호,, 애플 뮤직을 써본적은 없지만,, 그랬군?
팝업 버튼으로 상호 배타적인 옵션(mutually exclusive) 또는 상태의 무계층 목록(flat list)을 표시합니다. 팝업 버튼은 사용자가 자신의 콘텐츠에 영향을 미치는 선택을 할 수 있도록 도와줍니다.
👩🏻💻 애플 뮤직 Sort 옵션들 보면 [이름순 / 최근 추가순 / 최근 재생순 / Playlist Type 순]
이렇게 있는데, 서로 겹치지 않으니까 상호 배타적이라고 할 수 있겠고, 한 옵션이 다른 옵션에 속하거나 하지 않으니까 무계층 속성이라고 할 수 있겠군? 이런 경우에 팝업 버튼을 쓴단말이쥐~~
상호 배타적 — 두 대상이 어떤 속성에서 서로 다른 것 (출처 — [사회조사분석사] 상호배타적과 독립적의 차이점)
작업 목록(list of action) 을 제공하거나, 여러 항목을 선택하거나, 하위 메뉴(submenu)를 제공해야 하는 경우에는 pull-down button 을 사용하세요.
👩🏻💻 ㅇㅋ,, 저런 경우는 팝업 버튼이 아니라 풀 다운 버튼을 사용하기
유용한 기본 선택(default selection)을 표시합니다. 닫힌 팝업 버튼은 항상 현재 선택 항목을 표시하지만, 사용자가 아직 선택하지 않은 경우에는 기본 항목(default item)을 표시합니다. 가능하면 대부분의 사람들이 원하는 항목을 기본 선택 항목으로 지정하세요.
👩🏻💻 사용자가 메뉴에서 특정 옵션을 선택하기 전에는 보편적인 기본 옵션을 설정하란 말이군
공간이 제한되어 있고 항상 모든 옵션을 표시할 필요가 없는 경우 팝업 버튼을 사용하는 것은 좋은 선택입니다. segmented control 은 상호 배타적인 옵션 set 을 제공 하지만, 항상 모든 항목을 표시하기 때문에 일반적으로 팝업 버튼보다 더 많은 공간이 필요합니다.
👩🏻💻 아 맞지 맞지. segment control 은 옵션이 다 표시 되잖아? 굳이 모든 옵션을 표시할 필요가 없을때는 팝업 버튼 사용하라고~~?
이제 코드로!
좋아 이만하면 풀 다운 버튼이랑 팝업 버튼에 대한 개념은 대충 본거 같고,, 이제 코드,, 코드를 보자!
Pull Down 버튼 부터 스토리보드에 올려놔봤구여?
오호,, Button > Menu
하위에 Item1
과 Item2
가 children 으로 들어있네요. (코드에서도 button.menu?.childeren.first
처럼 Item1
에 대해 접근 가능)
참고로 이 상태로 빌드하고 버튼 클릭하잖아요?? 그럼 아무 메뉴도 안떠요; Item1
, Item2
로 button.menu.children
이 들어가있긴 하지만, 아마 해당 UIMenuElement
들에 대한 action이 들어가 있지 않기 때문일걸여??
튼 이제 버튼 연결을 해봅시다
엥?? 그냥 UIButton
Type 이라고?? 나는 뭐 타입이 다른 애인줄 알았지? 그럼 얘를 메뉴 버튼으로 만드는건 뭔 속성인데 대체????
바로 showsMenuAsPrimaryAction
과 changesSelectionAsPrimaryAction
속성에 의해 UIButton
이 우리가 아는 그냥 버튼이 될지, pull-down 버튼이 될지, pop-up 버튼이 될지 결정됩니다.
우선 showsMenuAsPrimaryAction
부터 살펴보져
아니,, 애플 놈들아 설명좀 써놔라;;
일단 적어보자면 showsMenuAsPrimaryAction
는 UIControl
의 인스턴스 프로퍼티로, button 을 클릭할 때 menu 를 바로 띄울지 여부를 결정합니다.
그러니까 iOS 14 부터 UIButton 의 인스턴스 프로퍼티로 menu
라는 친구가 생기면서
아래와 같이 버튼에 대한 메뉴를 설정할 수 있게 되었는데요,
let ok = UIAction(title: "확인", handler: { _ in print("확인") })let cancel = UIAction(title: "취소", attributes: .destructive, handler: { _ in print("취소") })let buttonMenu = UIMenu(title: "메뉴 타이틀", children: [ok, cancel])myButton.menu = buttonMenu
클릭하면 UI 는 뭐 이런식으로 나오고여
이때 showsMenuAsPrimaryAction
가 true
이면 클릭하자마자 바로 메뉴가 나오고여, false
이면 버튼을 길게 꾹 클릭해야 메뉴가 나옵니다. 후자의 경우는 iOS 14 이상부터 safari 의 하단 Toolbar button 을 길게 누르면 확인할 수 있어여.
ㅇㅋ 다음 changesSelectionAsPrimaryAction
으로 넘어가봅시다
얘는 iOS 15 에서 새롭게 등장한 UIButton
의 인스턴스 프로퍼티로 button 의 selection 을 추적 여부를 결정하는데, 자세한 동작은 위에서 살펴본 showsMenuAsPrimaryAction
과 menu
프로퍼티에 의해 결정됩니다.
1. menu = nil
& changesSelectionAsPrimaryAction = false
- 걍.. 우리가 아는 버튼..!
2. menu = nil
& changesSelectionAsPrimaryAction = true
changesSelectionAsPrimaryAction
이true
기 때문에 짧게 눌렀을때 버튼이 토글 됨
3. menu != nil
& changesSelectionAsPrimaryAction = true
& showsMenuAsPrimaryAction = false
changesSelectionAsPrimaryAction
이true
기 때문에 짧게 눌렀을때 버튼이 토글 됨- menu가 있지만,
showsMenuAsPrimaryAction
이false
기 때문에 메뉴는 길게 눌러야 나옴
4. menu != nil
& changesSelectionAsPrimaryAction = false
& showsMenuAsPrimaryAction = false
changesSelectionAsPrimaryAction
이false
기 때문에 짧게 눌렀을때 버튼 토글 안됨- menu가 있지만,
showsMenuAsPrimaryAction
이false
기 때문에 메뉴는 길게 눌러야 나옴
5. menu != nil
& changesSelectionAsPrimaryAction = false
& showsMenuAsPrimaryAction = true
changesSelectionAsPrimaryAction
이false
기 때문에 button 의 selection 을 추적 하지 않아 button title 에도 반영이 안됨- menu가 있고
showsMenuAsPrimaryAction
이true
기 때문에 버튼 클릭시 메뉴가 바로 나옴
이 속성 조합이 바로 pull-down button에 해당합니다. Interface Builder에서 pull-downp button을 올려놓으면 Button > Menu — Selection as Primary Action 에 미체크, Control > Menu — Show as Primary Action 에 체크 되어있는 것을 확인할 수 있습니다.
6. menu != nil
& changesSelectionAsPrimaryAction = true
& showsMenuAsPrimaryAction = true
changesSelectionAsPrimaryAction
이true
기 때문에selectedElements
속성을 통해 선택한 항목을 추적하며, 버튼 title은 선택을 반영하도록 업데이트 됨.- menu가 있고
showsMenuAsPrimaryAction
이true
기 때문에 버튼 클릭시 메뉴가 바로 나옴
이 속성 조합이 바로 pop-up button에 해당합니다. Interface Builder에서 pop up button을 올려놓으면 Button > Menu — Selection as Primary Action 에 체크, Control > Menu — Show as Primary Action 에 체크 되어있는 것을 확인할 수 있습니다.
마자여 사실 pull down 버튼 에서 changesSelectionAsPrimaryAction
이 false -> true 로 바뀐게 pop up 버튼이라고 할 수 있져
이렇게 changesSelectionAsPrimaryAction
값이 true
라면 선택한 메뉴 아이템이 체크 표시되고, button 의 title 이 변경(pop up button)되지만, false
라면 앞의 두가지 효과가 적용되지 않습니다 (pull down button).
비슷한 맥락으로 changesSelectionAsPrimaryAction = true
라면, UIMenuElement
의 state
가 .on
이어도, 선택한 항목에 대해서만 체크 표시가 됩니다. 즉 하나의 element 만 선택하도록 보장하는거져!!
let ok = UIAction(title: "확인", state: .on, handler: { _ in print("확인") })let cancel = UIAction(title: "취소", attributes: .destructive, state: .on, handler: { _ in print("취소") })let buttonMenu = UIMenu(title: "메뉴 타이틀", children: [ok, cancel])myButton.menu = buttonMenu
하지만 changesSelectionAsPrimaryAction = false
라면 state
가 .on
으로 설정된 모든 항목들에 대해 체크 표시가 됩니다.
아까 HIG 에서 풀다운 버튼을 사용할 때 중 하나가 여러 항목을 선택할때라고 써있었잖아여. pop up button 과 달리 changesSelectionAsPrimaryAction = false
기 때문에 여러 항목 선택이 가능하니까 그랬던거군여,, ㅇㅎ,,
여러 항목을 선택할때 이외에도, 작업 목록(list of action)이나 하위 메뉴(submenu)를 제공할때 풀 다운 버튼을 사용하라고 써있던거 기억 나시나요?? 사실 이 기능들은 changesSelectionAsPrimaryAction = true
인 버튼, 즉 pop up button 에서도 제공할 수도 있지만, 그냥 저런 상황에서는 풀 다운 버튼이 더 적합하니까 그걸로 써라,, 이런 의미로 쓰인 것 같습니다? 코드 상으로 안되는건 아니니까요,,?
흠 일단 제가 이해한건 이 정두?!
이제 집중력 다 떨어졌으니까 급하게 마무리하기~~!!! 틀린 부분 있으면 댓글 주세여!! 그럼 20000!