WWDC 21 ) Meet the UIKit button system
안녕하세요 :) Zedd입니다.
오늘은 iOS 15에서 굉장히 많이 업데이트가 된 UIButton들에 대해서 알아보겠습니다 👀
Meet the UIKit button system을 아주 빠르고 간단하게 요약해보겠습니다.
# Deprecated property/method in UIButton
영상에는 나오진 않지만...iOS 15에서 UIButton에 deprecated property/method가 많아졌습니다.
(Xcode 13 Beta 2기준입니다.)
reversesTitleShadowWhenHighlighted
adjustsImageWhenHighlighted 와 titleEdgeInsets 이런것들은 많이 썼는데... deprecate되다니!
참고하세요!
# What's New in UIButton
Xcode 12, 13 순입니다.
앞구르기 하면서 봐도 뭔가 Button 스타일이 늘어난 것을 볼 수 있는데요.
1. Plain, Gary, Tinted, Filled 스타일의 버튼이 추가되었습니다. (왼쪽 그림)
2. multiline text가 지원됩니다. (오른쪽 그림)
3. Dynamic Type을 기본적으로 지원합니다.
3번에 대해서 추가설명을 하자면..
Xcode 13 이전에서 Button을 추가하면 왼쪽 그림처럼 System 15.0으로 지정되어있어서
기본적으로 Dynamic Type 지원이 안됐었어요.
Xcode 13에서는 중간, 오른쪽 그림처럼 IB에서 Font설정이 아예 다 빠지고, Dynamic Type을 기본적으로 지원합니다.
4. subtitle을 지원합니다.
5. activity indicator를 지원합니다.
# Configuration
이 Button들은 각각 다른 Button이 아니라 다 UIButton입니다.
let button = UIButton(type: .system)
button.configuration = .filled() ✅
이렇게 configuration으로 설정이 가능합니다.
버튼에 타이틀을 줘봅시다.
button.setTitle("대충 타이틀", for: .normal) ✅
이렇게들 사용하실텐데요, 이제는 Configuration으로도 쌉가능한 부분이 되었습니다.
var configuration = UIButton.Configuration.tinted()
configuration.title = "대충 타이틀"
let button = UIButton(configuration: configuration, primaryAction: ...)
[결과]
configuration으로 정말 많은것들을 설정할 수 있는데
title, subtitle, attributedTitle, attributedSubtitle, titlePadding, titleAlignment, image....등을 설정할 수 있습니다.
var configuration = UIButton.Configuration.tinted()
configuration.image = UIImage(systemName: "heart")
configuration.imagePlacement = .trailing
configuration.subtitle = "대충 서브타이틀"
configuration.title = "대충 타이틀"
let button = UIButton(configuration: configuration, primaryAction: nil)
imagePlacement도 지정할 수 있다는거!
타이틀이랑 이미지 사이의 간격이 너무 좁네요,, 역시나 imagePadding도 있습니다.
var configuration = UIButton.Configuration.tinted()
configuration.image = UIImage(systemName: "heart")
configuration.imagePlacement = .trailing
configuration.imagePadding = 30 ✅
configuration.subtitle = "대충 서브타이틀"
configuration.title = "대충 타이틀"
let button = UIButton(configuration: configuration, primaryAction: nil)
titlePadding과 imagePadding은 왼쪽 그림을 참고해주세요.
# ConfigurationUpdateHandler
버튼 누름 → 오른쪽 그림처럼 Image가 변경되었으면 좋겠음.
그럴 때 ConfigurationUpdateHandler를 사용하면 됩니다.
button.configurationUpdateHandler = { button in
var config = button.configuration
config?.image = button.isHighlighted ? UIImage(systemName: "heart.fill") : UIImage(systemName: "heart")
button.configuration = config
}
이렇게요!
지금은 이미지만 업데이트 했지만..Configration에 있는 어떤 프로퍼티든 업데이트가 가능합니다.
[🚨주의🚨]
isHighlighted는 UIButton의 State이므로 해당 값이 변경될 때 ConfigurationUpdateHandler가 호출됩니다.
외부에 flag라는 변수가 있고, 다른 버튼에서 flag를 업데이트 해줍니다.
let secondButton = UIButton(configuration: secondConfig, primaryAction: UIAction(handler: { action in
self.flag.toggle()
}))
그리고 ConfigurationUpdateHandler에서 해당 flag값을 보고 이미지를 업데이트 해줍니다.
button.configurationUpdateHandler = { button in
var config = button.configuration
config?.image = ✅ self.flag ✅ ? UIImage(systemName: "heart.fill") : UIImage(systemName: "heart")
button.configuration = config
}
이렇게요.이렇게하면 ConfigurationUpdateHandler는 불리지 않는데요.
button의 state가 변경되지 않았기 때문입니다.
[ConfigurationUpdateHandler 호출하게 만드는 방법]
var flag = false {
didSet {
self.button.setNeedsUpdateConfiguration()
}
}
flag의 값이 변경되면, setNeedsUpdateConfiguration를 호출하게하면 됩니다.
그럼 대충 이렇게됨
# UIButton. 근데 이제 Activity Indicator를 곁들인
위에서도 잠깐 언급했지만 이제 Button에 Activity Indicator를 보여줄 수 있습니다.
showActivityIndicator만 true로 하기만 하면 됩니다.
(필요한 경우 이미지를 대체하기도 합니다.)
참고로 showActivityIndicator는 Configuration에 있는 프로퍼티이므로
configuration.showsActivityIndicator = true
이렇게 해주면 됩니다.
showActivityIndicator는 ConfigurationUpdateHandler안에 있으면 좋을 것 같네요.
# Semantic styling
semaintic styling을 사용하면 위와같은 프로퍼티 변경 후 추가적인 작업없이 오른쪽과 같은 state별 상태를 가지게 됩니다.
해당 프로퍼티들은 Configuration안에 있습니다.
# Pull Down Button / Pop Up button
Pull Down Button과 Pop Up Button도 라이브러리에 추가되었는데요.
Pull Down Button은 이번에 추가된건 아니고..iOS 14에 추가된 menu라고 보시면 됩니다.
관련 글은 iOS 14 + ) UIMenu on UIButton 를 참고해주세요
대충 이런겁니다...
그럼 Pop Up Button만 보면 되겠군요@!
Pop Up Button은 Pull Down Button과 비슷한데, 메뉴 요소중 하나만 선택되도록 한다는 것이 차이점입니다.
이렇게!
기존 Pull Down Button에서
let favorite = UIAction(title: "즐겨찾기", image: UIImage(systemName: "heart"), handler: { _ in print("즐겨찾기") })
let cancel = UIAction(title: "취소", attributes: .destructive, handler: { _ in print("취소") })
self.button.menu = UIMenu(title: "title입니다",
image: UIImage(systemName: "heart.fill"),
identifier: nil,
options: .displayInline,
children: [favorite, cancel])
self.button.showsMenuAsPrimaryAction = true ✅
self.button.changesSelectionAsPrimaryAction = true ✅
아래 두 코드만 추가해주면 됩니다.
타이틀도 알아서 바뀜...
아무도 안궁금할 것 같지만..제가 헷갈려서 씀
🙋 : ㅇ ㅑ!!!! iOS 14 + ) UIMenu on UIButton 에서
이렇게 체크표시 할 수 있었잖아.
Pull Down Button이랑 Pop Up Button이랑 다른게 좀 헷갈리는데..둘 다 똑같은거 아녀?
🧑💻 : 맞습니다!
let favorite = UIAction(title: "즐겨찾기", image: UIImage(systemName: "heart"), ✅ state: .on ✅ ,handler: { _ in print("즐겨찾기") })
state를 on으로 하면 왼쪽에 체크표시가 나왔었죠.
만약 action모두에 state를 on하면 위 오른쪽 그림처럼 체크 표시가 여러개 나올 수도 있었습니다.
하지만,
self.button.changesSelectionAsPrimaryAction = true // iOS 15부터 사용 가능
changesSelectionAsPrimaryAction를 true로 주게 되면
두 Action모두 state가 on이었어도
self.button.changesSelectionAsPrimaryAction = true // iOS 15부터 사용 가능
self.button.menu = UIMenu(title: "title입니다",
image: UIImage(systemName: "heart.fill"),
identifier: nil,
options: .displayInline,
children: [favorite, cancel]) ✅
여기에 먼저 추가된 것만 체크표시가 나게 됩니다. 지금은 favorite이 cancel보다 앞에 있으니
즐겨찾기가 체크 된 채로 나오게 됩니다.
만약 이 코드가 없다면
self.button.changesSelectionAsPrimaryAction = true // iOS 15부터 사용 가능
왼쪽 그림처럼 둘다 state가 on인 상태이니 둘 다 체크표시가 나오게 되고,
아까 오른쪽 gif처럼 Button title이 자동으로 바뀌지도 않게 됩니다.
# 그 외
영상에 나오진 않지만, 그냥 참고하면 좋은것들..
- Style은 IB에서 지정이 가능합니다.
- Corner Style도 IB에서 지정이 가능합니다.
[Fixed, Dynamic]
Dynamic Type에 따라 Corner Style을 조정하냐의 차이입니다.
[그 외]
참고 :