[WWDC17] iOS11에 맞게 앱 업데이트하기
iOS 11을 마주하면 단순하면서도 크게 변화된 모습을 볼 수 있습니다. 바로 iOS의 상징과도 같은 네비게이션바의 디자인이 바뀐 것입니다.
iOS 11 에서 새로 생긴 Files 라는 앱을 보죠.
커진 타이틀 바, 새로운 검색 바 - iOS 11 의 가장 눈에 띄는 특징입니다.
화면을 가로로 돌려보겠습니다.
얇아진 탭바와 새로운 배치의 탭아이템 버튼이 눈에 들어옵니다.
이번 글에선 위와 같은 iOS 11의 가장 눈에 띄는 UI 요소들에 대해서 살펴보겠습니다.
네비게이션바, 툴바, 탭바, 검색바 등 iOS를 이루는 핵심 UI요소인 Bar
메인 UI를 배치하기 위한 각종 margin, inset
스크롤뷰와 테이블뷰를 만들기 위한 새로운 법칙
UIBarItem
탭바, 네비게이션바, 툴바의 액션 버튼이 되는 UIBarItem 에 대해서 알아보겠습니다.
위 두 개의 스크린샷에서 보듯이 landscape 모드에서는 보다 작은 이미지를 쓸 수 있습니다.
다음 이미지를 보세요.
가운데 선택하려는 버튼의 이미지와 타이틀이 크게 보여지고 있습니다.
이건 평소에 모두에게 보이는 UI는 아닙니다.
저시력자를 위한 설정인 ‘텍스트 크게 보기’ 를 활성화 했을 때만 나타납니다.
(사용방법은 ‘아이폰 설정앱’ > ‘일반’ > ‘손쉬운 사용’ > ‘더 큰 텍스트’ > ‘글자 더 크게 조절’ 키고 아래 글자 크기 선택에서 더 큰 크기로 선택)
네비게이션바나 탭바에서 버튼을 살짝 누르고 있으면 위와 같이 현재 선택한 버튼에 대한 정보를 크게 보여주게 됩니다.
iOS 10까지는 텍스트를 크게 보기 했을 때에도 탭바나 네비게이션바 안의 텍스트까지 크게 보여주지는 않았기 때문에 저시력자의 경우 곤란한 경우가 있었습니다.
iOS 11에서는 이 부분을 커버하기 위해 새로운 보조 UI를 도입한 걸로 보입니다.
UIBarItem 을 설정하기 위한 핵심 api를 살펴보도록 하겠습니다.
// 타이틀
UIBarItem.title
// 이미지
UIBarItem.image
// landscape 용 이미지
UIBarItem.landscapeImagePhone
// 저시력자용 큰 이미지
UIBarItem.largeContentSizeImage
스토리보드를 이용할 경우 위와 같이 여러가지 이미지를 사용하지 않고, 하나의 pdf 파일을 사용할 수도 있습니다. 이 경우에는 Preserve Vector Data 를 활성화 시켜줍니다.
큰 타이틀바 만들기
네비게이션바에 큰 타이틀을 만들기 위해선 단 한 줄만 추가하면 됩니다.
navigationBar.prefersLargeTitles = true
하지만 요것만 설정하면 같은 네비게이션 컨트롤러에 연결되는 모든 뷰컨트롤러 화면에 큰 타이틀바가 적용이 됩니다.
기본적으로 큰 타이틀이 나오고 컨텐츠 영역의 스크롤 화면을 위로 올리게 되면 원래 모습처럼 작아집니다.
보통은 첫번째 두번째까지만 큰 타이틀이 필요하고 컨텐츠가 메인인 화면에서는 큰 타이틀이 필요 없을 경우가 더 많을 겁니다.
navigationItem.largeTitleDisplayMode
이 때는 navigationItem의 largeTitleDisplayMode 속성을 바꿔줍니다.
.automatic
, .always
, .never
세 가지 값이 있고 .automatic
이 기본값입니다.
.automatic
은 이전 화면의 모습을 따라갑니다.
.always
는 항상 큰 타이틀을 유지합니다.
.never
는 큰 타이틀로 보여지지 않고 이전과 같은 모습을 보여줍니다.
navigationBar: UINaivgationBar
는 UINavigationController
의 property 이고,
navigationItem: UINavigationItem
은 UIViewController
의 property 라는 것도 기억해 두세요.
검색바
새로운 검색바의 설정은 이전에 비해 매우 간단해졌으며, 디자인도 눈에 잘 띄게 바뀌었습니다.
navigationItem.searchController
navigationItem.hidesSearchBarWhenScrolling
searchController: UISearchController
만 설정해주면 검색 UI 를 자동으로 설정해 줍니다.
이전까지는 검색 UI 를 배치하는데 꽤 복잡한 면이 있었는데, iOS 11 에 오면서 매우 쉬운 방식으로 바뀌었습니다. :)
UINavigationBar와 UIToolbar 에서 Auto Layout 지원
이제 UINavigationBar와 UIToolbar 에 들어갈 커스텀 뷰에 Auto Layout 속성을 반영할 수 있습니다.
보다 정확히 얘기하면 커스텀 뷰의 Auto Layout 사이즈 속성을 반영시킬 수 있습니다.
intrinsicContentSize
를 이용하던가 width
, height
속성을 직접 주면 됩니다.
Layout Margins
UIView
의 layoutMargins: UIEdgeInsets
는 해당 뷰의 서브뷰들을 배치할 때 기준이 되는 가이드 역할을 합니다. 해당 margin안쪽에만 서브뷰들을 배치하도록 가이드합니다.
(layoutMargins
이 UI를 배치하기 위한 강제사항은 아닙니다.)
layoutMarginsGuide
는 layoutMargins
를 auto layout에서 이용하기 위한 값입니다.
iOS 11에서는 directionalLayoutMargins
가 새로 생겼으며, layoutMargins
대신 사용하길 권장합니다.
layoutMargins
는 UIEdgeInsets
로써 left
와 right
값을 가지지만, directionalLayoutMargins
는 NSDirectionalEdgeInsets
로써 leading
, trailing
값을 가지게 됩니다.
NSDirectionalEdgeInsets
도 iOS 11에서 도입되었습니다.
Directional 이란 개념은 iOS UI Programming에서 매우 중요합니다.
Left, Right 대신 Leading, Trailing 이라는 개념을 쓰며, 이는 오른쪽에서 왼쪽으로 글씨를 쓰는 나라들을 위해 중요합니다. 대부분의 경우 왼쪽에서 오른쪽으로 글씨를 쓰며, UI의 기준이 왼쪽이 되지만, 아랍어의 경우 오른쪽에서 왼쪽으로 글씨를 쓰며, 모든 UI도 오른쪽이 기준이 되어 거의 모든 요소들의 좌우가 뒤집히게 됩니다.
directionalLayoutMargins
의 trailing
값에 변화를 주게 되면, 일반적인 경우 오른쪽에 여백이 만들어지지만, 오른쪽이 기준인 언어 환경에서는 왼쪽에 여백이 만들어지게 됩니다.
layoutMargins
, directionalLayoutMargins
어느 한 쪽을 변경해도 두 값은 동기화됩니다. left
, right
, leading
, trailing
의 개념 차이를 확실히 알고 있어야 합니다.
iOS 11 이전까지는 layoutMargins
의 기본값을 제공했었습니다. (8,8,8,8) 이런 식으로 말이죠. 이제 이 값은 더 이상 제공하지 않습니다. 기본값은 .zero
입니다.
대신 새롭게 UIViewController
의 속성으로 systemMinimumLayoutMargins
가 생겼습니다.
layoutMargins
와 directionalLayoutMargins
는 systemMinimumLayoutMargins
를 기준으로 동작하게 됩니다.
잊지 마세요. layoutMargins
와 directionalLayoutMargins
는 UIView
의 속성이고, systemMinimumLayoutMargins
는 UIViewController
의 속성입니다.
하지만 systemMinimumLayoutMargins
를 무시하는 방법도 있습니다. UIViewController.viewRespectsSystemMinimumLayoutMargins
를 사용하면 됩니다.
systemMinimumLayoutMargins
를 무시하고 directionalLayoutMargins
를 .zero
로 만들면 모든 컨텐츠를 디바이스 스크린에 꽉 채울 수 있습니다.
LayoutGuide and SafeArea
topLayoutGuide
와 bottomLayoutGuide
가 deprecated 되었습니다.
대체된 api 를 알아보기 전에 이것들이 어떤 기능을 하던 api인지 알아보겠습니다.
iOS 7에서는 iOS가 디자인 개편이 되며, 콘텐츠를 강조하기 위해 콘텐츠를 풀스크린으로 배치하고 네비게이션바와 툴바를 투명하게 처리하는 방식을 쓰게 됩니다.
보통 이 콘텐츠는 스크롤이 되는 뷰가 되는데요.
실제로 네비게이션바나 툴바가 가리고 있는 영역이 어딘지 알기 위해서 topLayoutGuide
와 bottomLayoutGuide
가 필요했습니다.
edgesForExtendedLayout
은 콘텐츠 영역을 바 영역 아래까지 확장할 것인지 말지를 결정하는 기능을 합니다.
이제 더 이상 topLayoutGuide
와 bottomLayoutGuide
는 사용하지 않습니다.
대신에 Safe Area 라는 개념이 되었습니다.
topLayoutGuide
와 bottomLayoutGuide
는 UIViewController
의 속성이었지만, 새 Safe Area는 UIView
의 속성입니다.
말 그대로 이제 다른 디자인 요소가 현재 뷰를 침범하지 않는 영역은 Safe Area가 되고,
UIView.safeAreaInsets: UIEdgeInsets
UIView.safeAreaLayoutGuide: UILayoutGuide
이 두개의 요소로서 영역을 판단하게 됩니다.
네비게이션바나 툴바와 같은 기본 디자인 요소 이외에 위에 나온 그림과 같이 바 부분의 영역을 커스텀 디자인으로 확장해야 한다면 UIViewController.addtionalSafeAreaInsets 을 통해서 설정할 수 있습니다.
영역이 변경되게 되면 다음 두 개의 메소드가 호출되어 변경 여부를 알 수 있습니다.
UIView.safeAreaInsetsDidChange()
UIViewController.viewSafeAreaInsetsDidChange()
UITableView
다음은 테이블뷰입니다.
iOS 11에서는 테이블뷰에 중요한 변경점이 생겼습니다.
이제 UITableView
는 Self-Sizing 이 Default입니다.
다른 말로 하면 이제 테이블뷰는 Auto Layout 기반으로 만드는게 기본이라는 얘기입니다.
테이블뷰의 셀과 헤더뷰, 푸터뷰 등을 Auto Layout 으로 만들어야 합니다.
(물론, 이전 스타일로도 개발할 수 있습니다.)
참고로 UITableView
를 Auto Layout기반으로 하려면 UITableView.estimatedRowHeight
를 UITableViewAutomaticDimension
으로 설정하면 됩니다.
iOS 11에서는 이게 기본값이기 때문에 따로 설정할 필요가 없습니다. 대신 이전 스타일로 개발하려면 다음과 같이 설정해주세요.
override func viewDidLoad() {
tableView.estimatedRowHeight = 0
tableView.estimatedSectionHeaderHeight = 0
tableView.estimatedSectionFooterHeight = 0
}
다음은 테이블뷰의 Separator inset 에 대해 살펴보겠습니다.
먼저 iPad 에서 간단한 테이블뷰를 만들어 보겠습니다.
import UIKit
class ViewController: UIViewController {
override func loadView() {
view = UITableView()
}
override func viewDidLoad() {
super.viewDidLoad()
let tableView = view as! UITableView
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
tableView.dataSource = self
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = "Table view cell"
return cell
}
}
iPad 에서는 테이블뷰를 화면에 꽉 차게 배치했을 때 separatorInset
이 readable content guide 에 자동으로 맞춰집니다.
(iPad 는 좌우가 매우 넓어 콘텐츠를 좌우 꽉 차게 배치하면 한 번에 보기 어렵기 때문에, UIView.readableContentGuide
를 제공하여 적절한 배치 영역을 알려줍니다. 특히나 읽기를 주목적으로 하는 앱에서는 콘텐츠를 반드시 readableContentGuide
기준으로 배치하는 것이 좋습니다.)
이 상태에서 테이블뷰의 separatorInset 을 다시 설정하면 어떻게 될까요?
iOS11과 iOS11 이전의 상황이 다릅니다.
다음과 같은 코드를 추가하겠습니다.
// in viewDidLoad
tableView.separatorInset = UIEdgeInsets(top: 0, left: 50, bottom: 0, right: 0)
똑같은 코드지만 위와 같이 다르게 표현되는 걸 볼 수 있습니다. (좌우 inset뿐만 아니라 iOS11에서는 스테이터스바 영역과 겹치지 않게 자동으로 상단 여백을 설정해준 것도 볼 수 있습니다.)
iOS 10 에서는 separatorInsets 을 여전히 readable content guide 를 기준으로 inset 을 설정해주고,
iOS 11 에서는 개발자의 원래 의도대로 cell의 끝부분부터 inset을 적용합니다.
그러면 iOS 11에서 이전과 같이 readable content guide 기준으로 하려면 어떻게 해야 할까요?
// in viewDidload
tableView.separatorInsetReference = .fromAutomaticInsets
새로 생긴 separatorInsetReference 값을 automatic 으로 지정해주면 됩니다.
separatorInsetReference: UITableViewSeparatorInsetReference 는 두 개의 enum 값을 가집니다.
public enum UITableViewSeparatorInsetReference : Int {
// The value set to the separatorInset property is interpreted as an offset from the edges of the cell.
case fromCellEdges
// The value set to the separatorInset property is interpreted as an offset from the automatic separator insets.
case fromAutomaticInsets
}
.fromCellEdges 가 기본값이기 때문에 위와 같은 현상이 일어남을 알 수 있습니다.
테이블뷰를 디자인하기 위한 cell 에 대해서 간단한 사실을 몇 가지 알려드리겠습니다.
- UITableViewCell, UITableViewHeaderFooterView 의 contentView 는 항상 safe area 안에 있습니다. 반드시 contentView를 사용하세요.
- 테이블뷰의 헤더와 푸터, 테이블섹션의 헤더와 푸터에 모두 UITableViewHeaderFooterView를 사용하세요.
마지막으로 테이블뷰의 스와이프 액션이 iOS 11에서 풀 지원되게 되었습니다.
정말 많은 개발자가 기다려왔던 기능입니다.
좌, 우에 여러개의 액션을 넣을 수 있고, 아이폰 메일앱에 있는 풀 스와이프로 지우기도 가능해졌습니다.
iOS는 메이저 버전이 업데이트 될 때마다 UI를 잘 만들 수 있도록 계속해서 api들이 발전에 발전을 거듭하고 있습니다.
이번에는 큰 어려움 없이 쉽게 중요한 기능들을 구현할 수 있도록 애플 개발자들이 많이 애써준 모습이 보입니다. :)
이 글은 WWDC17의 세션 Updating Your App for iOS 11 을 정리한 글입니다.