WWDC 2017 Building Apps with Dynamic Type를 보다가 마침 현재 프로젝트에도 있는 문제가 있어서 적용 가능해보이는 부분을 발견했다.
바로 UILabel
이 나란히 2개 있는 상황에서 기기의 폰트 크기가 커짐에 따라 어떻게 레이아웃을 잡아야하는지에 대한 내용이었다.
당연히 텍스트 일부가 ...
등으로 짤리면 안되고, 뭔가 텍스트의 줄바꿈이 이상하게 되어있어도 안된다고 이야기하고 있다. 딱 봐도 이상해보이긴 한다...
그래서 권장하는 방식은 텍스트 크기가 커졌을 때, 우측 UILabel
을 좌측 UILabel
아래로 내리는 방법이다!
WWDC에서는 아래와 같은 방법으로 구현할 수 있다고 이야기하고 있다.
우측 UILabel
의 firstBaseline을 좌측 UILabel
lastBaseline에 맞추는데, 바로 아래 딱 붙게해줄 수 있도록 constriantEqualToSystemSpacingBelow
를 주고 있는 것을 볼 수 있다.
근데 공식문서를 보면 아래와 같이 사용하고 있는 것 같다.
constraint(equalToSystemSpacingBelow:multiplier:)
괄호의 위치가 조금 다른 느낌...?
아무튼 이렇게 레이아웃을 주면 vertical하게 위치시킬 수 있다고 한다. 이제 어떻게 시스템 폰트 크기를 식별하느냐인데, 이 또한 WWDC에서 알려주고 있다.
두 가지 방법이 있는데, 하나는 isAccessibilityCategory
를 사용하는 방법, 다른 하나는 특정 글자 크기를 지정해서 그걸 넘어서는지 확인하는 방법이다.
isAccessibilityCategory
의 문서를 보면accessibilityMedium
, accessibilityLarge
, accessibilityExtraLarge
, accessibilityExtraExtraLarge
와 accessibilityExtraExtraExtraLarge
만 true의 값을 반환한다고 한다.
그래서 우선은 이를 활용해서 레이아웃을 따로 잡아주었다.
자 그러면 한 번 구현해보자!!
우선 레이아웃을 담아줄 변수를 생성하자.
var LargeFontTypeLayout: [NSLayoutConstraint]?
var standartFontTypeLayout: [NSLayoutConstraint]?
isAccessibilityCategory
여부에 따라 나뉘는 두 가지 케이스에 맞게 레이아웃을 잡아주기 위해 두 가지 레이아웃 배열을 만들어줬다.
각 케이스에서만 특수하게 적용해줄 레이아웃들을 각 배열에 담아준다. 공통으로 적용되는 레이아웃들은 굳이 배열에 담지 않고도 바로 적용시켜줘도 된다.
LargeFontTypeLayout = [
second.firstBaselineAnchor.constraint(equalToSystemSpacingBelow: first.lastBaselineAnchor, multiplier: 1),
second.leadingAnchor.constraint(equalTo: first.leadingAnchor)
]
standartFontTypeLayout = [
second.centerYAnchor.constraint(equalTo: first.centerYAnchor),
second.leadingAnchor.constraint(equalTo: first.trailingAnchor, constant: 10)
]
자 이제 각 케이스에 따라 담아준 레이아웃 배열을 active시켜주면 된다.
if traitCollection.preferredContentSizeCategory.isAccessibilityCategory {
LargeFontTypeLayout?.forEach {
$0.isActive = true
}
} else {
standartFontTypeLayout?.forEach {
$0.isActive = true
}
}
자 여기까지 잡아준 레이아웃 코드를 메서드로 잘 정리해서 viewDidLoad()
에 넣어주면 앱이 처음 실행되었을 때 기기의 폰트 크기에 따라 잘 분기가 될 것이다.
하지만 중간에 앱을 백그라운드로 보내고, 기기의 폰트 크기를 바꾸게 되면 현재 코드로서는 대응하지 못하게 된다.
그렇기에 추가해줘야하는 코드가 있는데, 바로 기기의 폰트 설정이 바뀌었다는 정보를 받기 위해 NotificationCenter에 addObserver
를 해줘야 한다.
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(setLayoutByDynamicType), name: UIContentSizeCategory.didChangeNotification, object: nil)
}
// setLayoutByDynamicType
@objc func setLayoutByDynamicType() {
if traitCollection.preferredContentSizeCategory.isAccessibilityCategory {
standartFontTypeLayout?.forEach {
$0.isActive = false
}
LargeFontTypeLayout?.forEach {
$0.isActive = true
}
} else {
LargeFontTypeLayout?.forEach {
$0.isActive = false
}
standartFontTypeLayout?.forEach {
$0.isActive = true
}
}
}
시스템에서 폰트 카테고리가 바뀌었을 때 post를 주는데 그 채널은 UIContentSizeCategory.didChangeNotification
이다. 이에 기본적으로 제공해주는 채널에 대해 addObserver
를 해두고 알림을 받았을 때 실행할 메서드를 작성해준다.
다른 것은 없고 두 케이스의 레이아웃 배열을 스위치 온/오프 하듯이 원하지 않는 레이아웃을 deactive해주고, 원하는 레이아웃을 active해주면 된다.
자 이제 결과물을 확인해 볼 시간...!
이제 텍스트 크기에 맞게 레이아웃이 자동으로 적용되는 것을 볼 수 있다.
다 하고 알게된 사실인데... 만약 저 두 개의 UILabel
을 UIStackView
로 묶어도 되는 상황이라면 더 쉽게 해결해 볼 수 있다...
글자가 커지는 경우 .axis
만 vertical
로 바꿔주고, 기본인 경우 다시 horizontal
로 바꿔주면 된다.
뿐만 아니라 필요하고, 필요하지 않는 레이아웃을 아까 봤던 방법처럼 active/deactive해서 적용해주면 더 간단하게 해결해 볼 수도 있을 것 같다.
끝!
Ref
https://developer.apple.com/videos/play/wwdc2017/245/
https://developer.apple.com/documentation/uikit/nslayoutyaxisanchor/2866022-constraint
https://a11y-guidelines.orange.com/en/mobile/ios/wwdc/2017/245/