• Key features
    • Document launch experience
      • 런치 뷰 디자인을 완전히 통제 가능
      • 템플릿 생성을 지원
      • Evolve your document launch experience 세션 참조
    • Updated tab and sidebar
      • iPadOS 18부터 탭바 디자인이 컴팩트하게 변경

        스크린샷 2024-06-14 오후 12.30.14.png

      • 기존 사이드바도 새로운 UITabBarController API를 통해서 사이드바와 탭바를 통합할 수 있다.

        • 사이드바를 최소화하면 탭바로 자동적으로 애니매이션이 수행된다.
      • 드래그 앤 드롭으로 커스텀 기능도 제공

      • UITab, UITabGroup

      • Elevate your tab and sidebar experience in iPadOS

    • Fluid transitions
      • 새로운 Zoom 트랜지션
        • 네비게이션과 프레젠테이션 모두에 적용
        • 인터렉티브하고 중간에 멈출 수도 있다.
  • UIKit and SwiftUI Interoperability
    • animation

      • SwiftUI의 Animation을 UIKit의 animation에 사용 가능

        switch gesture.state {
        case .changed:
            UIView.animate(.interactiveSpring) {
                bead.center = gesture.translation
            }
        
        case .ended:
            UIView.animate(.spring) {
                bead.center = endOfBracelet
            }
        }
        
    • gesture recognizer

      • UIKit과 SwiftUI의 제스쳐 시스템 통합 완료

      • SwiftUI의 제스처를 UIKit에서 참조하기

        // Inner SwiftUI double tap gesture
        
        Circle()
            .gesture(doubleTap, name: "SwiftUIDoubleTap")
        
        // Outer UIKit single tap gesture
        
        func gestureRecognizer(
            _ gestureRecognizer: UIGestureRecognizer, 
            shouldRequireFailureOf other: UIGestureRecognizer
        ) -> Bool {
            other.name == "SwiftUIDoubleTap"
        }
        
      • 기존의 UIKit gestureRecognizer는 UIGestureRecognizerRepresentable 프로토콜을 통해서 사용 가능

    • 더 자세한 내용은 What’s new in SwiftUI 참조

  • General enhancements
    • Automatic trait tracking

      • 기존에는 trait변경을 수동으로 구독했어야 했다.

        class MyView: UIView {
        	override init(frame: CGRect) {
        		super.init(frame: frame)
        		registerForTraitChanges([UITraitHorizontalSizeClass.self],
        			action: #selector(UIView.setNeedsLayout))
        	}
        	
        	override func layoutSubviews() {
        		super.layoutSubviews()
        		
        		if traitCollection.horizontalSizeClass == .compact {
        			// apply compact layout
        		} else {
        			// apply regular layout
        		}
        	}
        }
        
      • 이제는 일반적인 뷰나 뷰컨트롤러 업데이트 메소드(ex. layoutSubviews, drawRect) 등에서 어떤 trait을 참조하는지 기록했다가 관련 trait이 바뀌면 자동적으로 무효화를 한다(ex. setNeedsLayout, setNeedsDisplay)

      • 꼭 필요한 것만 의존함으로써 최대한의 성능을 이끌어 낼 수 있다.

      • 어떤 메소드가 지원하는지는 문서 참조

    • List improvements

      • UITraitCollection에 listEnvironment 추가

        • 셀이 어떤 리스트 안에 있는지를 보고 그에 맞게 스타일링 가능
        • UIListContentConfiguration과 UIBackgroundConfiguration이 활용
      • ex. 파일 앱

        • portrait모드에서는 insetGrouped
        • landscape에서는 같은 뷰가 splitView의 sidebar에 표시된다.
        • Compositional Layout을 사용할 때 이를 구분했어야 했다.
      • iOS 17 구현

        • appearance가 바뀔 때마다 이걸 호출해줬어야 한다.
        func configurations(for location: FileLocation,
        										listAppearance: UICollectionLayoutListConfiguration,Appearance)) ->
            (UIListContentConfiguration, UIBackgroundConfiguration) {
        		
        		let isSideBar = listAppearance == .sidebar
        
            var contentConfiguration: UIListContentConfiguration
            let backgroundConfiguration: UIBackgroundConfiguration
        	
        		contentConfiguration = isSidebar ? .sidebarCell() : .cell()
        		backgroundConfiguration = isSidebar ? .listSidebarCell() : .listGroupedCell()
        
            contentConfiguration.text = location.title
            contentConfiguration.image = location.thumbnailImage
        
            return (contentConfiguration, backgroundConfiguration)
        }
        
      • iOS 18 이후

        • 이제는 traitCollection이 바뀔 때마다 알아서 바꿔준다.
        func configurations(for location: FileLocation) ->
            (UIListContentConfiguration, UIBackgroundConfiguration) {
        
            var contentConfiguration = UIListContentConfiguration.cell()
            let backgroundConfiguration = UIBackgroundConfiguration.listCell()
        
            contentConfiguration.text = location.title
            contentConfiguration.image = location.thumbnailImage
        
            return (contentConfiguration, backgroundConfiguration)
        }
        

        스크린샷 2024-06-14 오후 1.03.08.png

    • Update Link

      • 주기적으로 UI업데이트가 필요한 경우에 사용

      • CADisplayLink와 비슷하지만 더 많은 기능을 가진다.

        • view tracking: visible한 window에 추가되면 활성화되고 뷰가 제거되면 자동으로 비활성화 된다.
        • low latency applications
        • 더 나은 성능과 배터리 효율 제공
        • 더 자세한 기능은 문서 참조
        let updateLink = UIUpdateLink(
            view: view,
            actionTarget: self,
            selector: #selector(update)
        )
        updateLink.requiresContinuousUpdates = true
        updateLink.isEnabled = true
        
        @objc func update(updateLink: UIUpdateLink,
                          updateInfo: UIUpdateInfo) {
            view.center.y = sin(updateInfo.modelTime)
                * 100 + view.bounds.midY
        }
        
    • Symbol animations

      • sf symbol에 새로운 애니매이션 추가

        • .wiggle
        • .breathe
        • .rotate
      • 새로운 반복 옵션

        • .periodic(반복횟수, 반복 간 딜레이 지정)
        • .continuous
      • 기존 옵션 강화

        • replace 옵션이 magic replace가 가능하다면 그렇게 동작

          • badge와 slash 간의 전환이 부드러워짐
        • 불가능한 경우 fallback으로 동작

          • 기본값은 .downUp인데, 커스텀 가능
          .replace.magic(fallback: .upUp)
          
      • 새로운 SF Symbols 앱과 관련 세션 참조

    • Sensory feedback

      • iPadOS 17.5에서 Apple Pancil Pro와 Magic Keyboard지원추가
        • UICanvasFeedbackGenerator를 사용

          @ViewLoading var feedbackGenerator: UICanvasFeedbackGenerator
          
          override func viewDidLoad() {
              super.viewDidLoad()
              feedbackGenerator = UICanvasFeedbackGenerator(view: view)
          }
          
          func dragAligned(_ sender: UIPanGestureRecognizer) {
              feedbackGenerator.alignmentOccurred(at: sender.location(in: view))
          }
          
        • 플랫폼에 따라서 햅틱, 오디오, 둘 다, 혹은 아무것도 없을 수도 있다.

    • Text Improvements

      • UITextView에 textAttributes를 커스텀할 수 있는 기능 추가

        스크린샷 2024-06-14 오후 1.17.37.png

      • 하이라이트 관련 attribute추가

        var attributes = [NSAttributedString.Key: Any]()
        
        // Highlight style
        attributes[.textHighlightStyle] = 
        NSAttributedString.TextHighlightStyle.default
        
        // Highlight color scheme
        attributes[.textHighlightColorScheme] =
        NSAttributedString.TextHighlightColorScheme.default
        
      • 편집 패널도 커스텀 가능

        // UITextFormattingViewController.Configuration
        textView.textFormattingConfiguration = .init(groups: [
            .group([
                .component(.fontAttributes, .mini),
                .component(.fontPicker, .regular),
                .component(.textColor, .mini)
            ]),
            .group([
                .component(.fontPointSize, .mini),
                .component(.listStyles, .regular),
                .component(.highlight, .mini)
            ])
        ])
        
      • 쓰기 도구 지원

        • UITextView에 지원이 내장됨
        • 이 동작을 추적하고 제한할 수 있는 추가 API도 지원

        스크린샷 2024-06-14 오후 1.21.43.png

    • Menu actions

    • Apple Pencil