- External Display Support -> 관련 세션 WWDC 2011(구할 수 있나…?)
- 자동으로 미러링은 제공된다. -> 하지만 더 잘할 수 있다.
- Keynote 앱, 게임 등 - 외장 디스플레이에 프레젠테이션, 디바이스에 컨트롤
- 외장 디스플레이는 폰과는 정 반대의 특성을 가진다.
- Wide, public, no interaction
- 외장 디스플레이를 감지하는 법
if UIScreen.screens.count > 1 {
/// 외장 디스플레이 있음
}
/// 시스템 노티피케이션
UIScreen.didConnectedNotification
UIScreen.didDisconnectedNotification
// didConnectedNotification 콜백 내에서
if let externalScreen = UIScreen.screens.last {
externalWindow = UIWindow()
externalWindow.screen = externalScreen
configurationExternalWindow(externalWindow) // 루트뷰컨트롤러 만들어서 윈도우에 할당하는 작업
externalWindow.isHidden = false
}
// didDisconnectedNotification 콜백 내에서
externalWindow.isHidden = true
externalWindow = nil
if isSingleDisplayMode {
photeViewController.photo = photo
navigationController?.pushViewController(photoViewController, animated: true)
} else {
showOnExternalDisplay(photo)
}
- Layout-Driven UI -> UI의 복잡성을 관리하는 법
- 코어 컨셉
- UI에 영향을 미치는 상태를 찾고 추적하라
- 상태가 바뀌게 되면 레이아웃에 변경을 알리기 위해 setNeedsLayout()을 호출한다.
- UI를 상태에 맞게 layoutSubvies()에서 업데이트 해준다.
- Layout-animation-gesture가 조화롭게 돌아가도록 한다.
class MyView: UIView {
//
let coolView = CoolView()
var feelingCool = true {
didSet {
setNeedsLayout()
}
}
override func layoutSubviews() {
super.layoutSubviews()
coolView.isHidden = !feelingCool
}
}
- 애니메이션 - UIViewPropertyAnimator를 쓰면 복잡한 설정이 가능하다
var cardsInDeck = [CardView]() {
didSet {
setNeedsLayout()
}
}
func putCardInDeck(_ card: CardView) {
cardsInDeck.append(card)
UIView.animate(withDuration: 0.3,
delay: 0,
options: [.beginFromCurrentState],
animations: {
self.layoutIfNeeded()
}, completion: nil)
}
- Gestures
- UIGestureRecognizer는 여러 구체 클래스를 제공하고, 커스텀도 가능하다.
- Gesture의 종류
- Discrete: fire-and-forget 타입
- Continuous: 연속된 제스쳐. Began-Changed-Ended의 상태로 나눠짐
- UIPanGestureRecognizer -> WWDC14(Building Interruptible…)
- 드래깅을 위해서는 transitionInView()를 활용하라
- 애니메이션 핸드오프를 위해서는 velocityInView()를 사용하라
var cardsToOffSets = [CardView: CGPoint] {
didSet {
setNeedsLayout()
}
}
@objc func handleCardPan(_ pan: UIPanGestureRecognizer) {
if let card = pan.view as? CardView,
let currentOffset = cardsToOffsets[card] {
let transition = pan.transition(in: self)
cardsToOffsets[card] = CGPoint(x: currentOffset.x + transition.x, y: currentOffset.y + transition.y)
pan.setTransition(.zero, in: self)
}
}
override func layoutSubviews() {
super.layoutSubviews()
for card in cards {
if let offset = cardsToOffsets[card] {
card.frame.origin = offset
}
}
}
- Laser-Fast Launches
- 런치 과정
- 프로세스 포킹(fork, exec) -> iOS에서 알아서 해준다.
- 동적 링킹
- 과정
- 실행을 위한 메모리 할당
- 라이브러리와 프레임워크 링킹
- Swift, Objective-C, Foundation 등의 초기화(시스템 라이브러리)
- 정적 객체 초기화
- 이 과정들의 런치 타임의 4~50%를 차지한다.
- 하지만 이 과정에서 사용자 코드는 하나도 실행되지 않는다.
- 그래서 이 과정에서 시간을 줄이기 위해서는?
- 코드 중복을 최대한 제거하라
- 서드파티 라이브러리 사용을 줄여라.
- 시스템 라이브러리는 캐시가 되어 있어서, 모든 앱이 거기서 가져다 쓴다. 하지만 서드파티 라이브러리는 아무리 같은 버전을 써도 각자 쓴다.
- 정적 초기화 구문(load, initialize)에서 큰일을 하지 말 것
- UI 구성
- 과정
- UI 준비
- 상태 복구(Store restoration)
- 설정 불러오기(Load preferences)
- Load model data
- 이 과정을 빠르게 하려면
application(_: willFinishLaunchingWithOptions:)
application(_: didFinishLaunchingWithOptions:)
applicationDidBecomeActive(_:)
- 디스크 쓰기 작업은 피할 것
- 엄청 큰 데이터셋을 불러오는 작업은 피할 것
- 데이터베이스는 최대한 깔끔하게 유지할 것(불필요한 데이터가 계속 남아 있다거나 하면 안좋다.)
- 첫 프레임 만들기
- 과정
- Core Animation이 첫 프레임을 렌더링한다.
- 텍스트 드로잉
- 이미지 로딩과 디컴프레션을 한다
- 이 과정을 빠르게 하려면?
- 꼭 필요한 것만 할 것
- 뷰나 레이어를 숨겨 놓는 건 하지 말라
- 추가 런치 액션
- 런치 이후로 미뤄진 작업
- 앱이 반응할 수 있지만, 아직 사용할 수는 없는 단계
- 여기서는 런치 단계에서 필요한 것을 우선적으로 불러올 것
- 네트워크 상태가 좋지 않은 경우를 고려해서 UI를 짤 것
- 또한 이를 주기적으로 측정하라 - Time Profiler
- Smooth Scrolling
- 내 앱이 왜 느려질까?
- 과도한 계산
- Time Profiler를 사용해서 측정할 것
- 콜렉션 뷰와 테이블 뷰의 프리패칭 기능을 활용할 것
- 최대한 많은 작업을 백그라운드 큐에서 하자
- 네트워크와 파일 시스템 접근
- 이미지 드로잉
- 텍스트 사이징
- 그래픽 시스템의 문제
- Core Animation Instruments로 검사하
- Visual Effect와 Masking Cliping을 많이 쓰면 느려진다. 이 두가지는 절제할 것
- Continuing with Continuity
- 기기를 오가면서 작업을 이어갈 수 있는 능력-Handoff
- NSUserActivity를 사용하면 가능하다.
let activity = NSUserActivity(activityType: "com.apple.developer.video")
activity.title = "Adding Delight to your iOS App"
activity.isEligibleForHandoff = true
activity.usernifo = ["session-id": "2018-223", "currentTime": 2340]
userActivity = activity // UIViewController의 프로퍼티