ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [iPadOS] iPadOS에서 multiple scene을 구성하기
    프로그래밍/iOS 2021. 9. 22. 03:32

    최종 결과

     

    새로운 scene을 생성하는 방법을 이해하기

    scene과 그 구체적인 코드를 알아보기 전에, project settings에서 Supports Multiple Windows 체크박스를 활성화 했을 때 scene들을 생성할 수 있는 여러 방법들을 알아봅시다. 우리는 기본으로 제공되는 기능을 살펴보는 것뿐만 아니라, 실제 유저들이 app이 multiple window를 지원할 때 어떤 동작들을 기대하는지 보여드리고자 합니다. 그 뒤에 우리의 app에 어떻게 multiple window를 구현하는지 살펴보겠습니다.

    새로운 scene을 여는 기본적인 방법

    iPadOS에서는 새로운 scene을 여는 2가지 방법이 있습니다.

    첫번째로 앱이 이미 실행중인 상태에서 dock에 있는 앱 아이콘을 위로 끌어올려서 새로운 scene을 만드는 방법입니다. 만약 앱이 현재 화면에서 보여지고 있지 않을 때는, iPadOS는 활성된 scene을 움켜쥐고 앱 아이콘을 드롭한 위치에 이동시킵니다. 유저들은 다른 앱과 함께 당신의 앱을 flyover 방식으로 보여지게 할 건지 아니면 side-by-side로 할건지 선택할 수 있습니다.

     

    두번째 방법은 Exposè에서 앱을 선택하고 오른쪽 상단에 있는 + 버튼을 선택하는 것 입니다.

    Expose 화면

    (Expose에 대한 설명: https://www.macrumors.com/how-to/use-app-expose-ipados/)

    (App Switcher에 대한 설명: https://support.apple.com/ko-kr/guide/ipad/ipad619935ea/ipados)

    💡 expose ≠ app switcher 임에 주의하기
    💡 app switcher는 일반적으로 멀티테스킹 화면을 말하는 것이고, expose는 한 앱에 대해서만 화면들을 보여주는 것임. 위 사진은 Safari에 대한 expose 화면임. Safari를 연 상태에서 dock바에서 한번 더 Safari 앱 아이콘을 누르면 expose로 이동함.

     

    이렇게하면 앱의 default scene을 사용하여 새로운 full-screen scene을 생성하게 됩니다. 이 다음에 유저들은 window를 재정렬하거나 side-by-side로 보여지게 하거나 fly-over로 바꾸어서 보여지게 할 수 있습니다. 이것은 같은 동작으로 작동하지만 iOS 12까지는 모든 앱이 1개의 scene만 가질 수 있고 iOS 13부터는 여러개의 scene을 가질 수 있습니다.

     

    이 2가지의 기본 방법들 외에도 multiple scene을 지원하는 앱에서 사용자가 기대할 수 있는 특별한 인터렉션 패턴이 있습니다. 그건 바로 drag and drop 입니다.

    유저가 기대하는 Drag and Drop 인터렉션

    유저들은 pattern을 좋아하고, 이것은 애플도 마찬가지 입니다. 이러한 유저가 기대하는 상호작용의 예를 확인해 볼 수 있는 좋은 곳은 iPadOS에 애플이 기본으로 탑재한 앱 입니다. 애플이 만든 multi scene을 지원하는 앱들을 살펴보면 많은 곳에서 새로운 scene을 만드는 작업에서 drag and drop을 지원하는 것을 확인할 수 있습니다. Safari에서는 tab을 선택해서 screen의 양 끝으로 드래그해서 새로운 scene을 만들어 낼 수 있습니다. Contacts에서는 연락처 리스트에서 연락처를 grab해서 screen의 양 끝에 drop하면 새로운 scene에서 drag했던 연락처를 열 수 있습니다. 마지막으로 Mail에서는 이메일 작성 modal을 drag해서 screen의 양 끝에 drop하면 새로운 scene에서 작성 화면을 열 수 있습니다.

     

    이러한 인터렉션들은 매우 자연스럽습니다. 따라서 당신의 앱도 애플에서 구현한 방식과 유사한 인터렉션 pattern을 가지는 것도 충분히 고려해볼만 합니다.

     

    이제 유저가 새로운 scene을 열면서 기대하는 것에 대해서 알아보았으니, 1가지 이상의 scene 타입을 구성하는 방법에 대해서 알아봅시다.

    앱에서 지원하는 scene들 정의

    Xcode 11에서 프로젝트를 시작하고 Supports multiple windows checkbox를 활성화하세요. 당신은 이미 multi scene 구성을 위한 몇 개의 단계를 끝마쳤습니다. 이제 앱은 1개 이상의 active scene을 가질 수 있고, iOS는 scene을 구성하기 위해서 항상 당신의 SceneDelegate를 사용할 것 입니다.

    계속해서 진행하기 전에, 여기에 있는 샘플 프로젝트에 있는 Start 폴더의 프로젝트 파일을 실행하세요.

    이 프로젝트는 간단한 고양이 사진 뷰어 앱 입니다.

     

    준비가 끝났다면 계속 진행합시다.

    지금도 충분히 고양이 사진은 귀엽지만, detail page를 나란히 볼 수 있다면 더 귀여울 것 입니다. detail page가 tap될 때, 우리는 새로운 scene을 열어서 detail page를 볼려고 합니다. 지금 우리의 동작은 일반적인 앱에서 사용하는 동작과 다르다는 것에 주의하세요. 일반적으로 단순히 tap을 하면 현재 scene에서 detail page가 push되어 나타나게 하고, 고양이 사진을 screen 옆 끝으로 drag하면 새로운 scene을 열 수 있게 합니다. 우리는 drag and drop 동작을 이 포스트의 마지막에서 구현할 것 입니다. 지금은 단순히 multiple scene 지원에 대해서만 생각합시다.

     

    샘플 프로젝트를 살펴보면 2번째의 SceneDelegate(CatSceneDelegate)를 가지고 있다는 것을 확인 할 수 있습니다. 이 특별한 SceneDelegate는 Xcode가 기본으로 생성하는 SceneDelegate와 비슷해 보이지만, 이 CatSceneDelegate는 app의 main view를 사용하는 것 대신에 cat detail page를 사용합니다.

     

    당신의 앱이 새로운 SceneDelegate를 사용하고 있다는 것을 인지시키기 위해서, 반드시 Application Scene ManifestScene Configuration 배열에 새 항목을 추가시켜야 합니다.

     

    scene configuration은 단순히 string identifier에 불과합니다. 이 string identifier는 당신의 앱이 새로운 scene을 만들게 될 때 사용되는 scene delegate reference 입니다. 기본 scene configuration은 Default Configuration 으로 불리며 유저들에게 보여질 scene을 구성할 때 $(PRODUCT_MODULE_NAME).SceneDelegate 객체로 사용됩니다.

    💡 iOS는 앱을 실행하는 동안 당신의 scene delegate를 찾을 수 있어야합니다. 따라서 당신의 scene delegate의 class 이름을 반드시 $(PRODUCT_MODULE_NAME). 접두사를 사용해야 합니다.

     

    cat detail scene configuration을 추가하기 위해서, Application Session Role 의 옆에 있는 + 버튼을 클릭하세요. 클릭하면 Xcode는 새로운 configuration 항목을 만들 것입니다. Storyboard NameClass Name 필드를 삭제하세요. Delegate Class Name 으로 $(PRODUCT_MODULE_NAME).CatSceneDelegate 을, Configuration Name 으로 Cat Detail 을 입력하세요. Default Configuration 항목이 Cat Detail Configuration 보다 위에 있도록 드래그 하세요. 이제 configuration은 아래의 사진과 같게 되어야 합니다.

    앱을 실행하면 정상적으로 작동해야합니다.(아직 어떠한 scene들을 생성하지 않았으므로).

    다음 단계로 넘어가 봅시다!

    Scene의 열기, 닫기, 갱신

    일반적인 앱에서, 당신은 앱이 새로운 scene을 열거나 닫는 것을 원할 때가 있을 것 입니다. 어떻게하면 scene을 열거나 닫거나 갱신하거나 할 수 있을까요? 그리고 왜 이러한 동작을 하기를 원할까요? 이번 섹션에서는 이러한 질문에 대해서 알아보겠습니다. 먼저 새로운 scene을 열고 닫기 위한 logic 부터 시작합시다. 그 다음에 새로운 scene이 열려야 하는지 또는 기존 scene을 재사용할 수 있는지 결정하는 logic에 대해서 알아볼 것 입니다. 마지막으로 우리는 scene 갱신(refreshing)에 대해서 알아보겠습니다.

     

    새로운 scene을 열기위해서는 UIApplication 객체에 request를 해야합니다. UIApplication 은 새로운 scene session이 생성되어야 하는지, 아니면 기존 scene을 사용할 수 있는지 확인할 것 입니다. 이러한 부분이 샘플 코드에서 didTapCat(_:) 함수에 있습니다. 이 함수에 아래의 코드를 추가해서 cat detail page를 새로운 scene에서 열도록 합시다.

    let activity = NSUserActivity(activityType: "com.donnywals.viewCat")
    activity.userInfo = ["tapped_cat_name": tappedCat(forRecognizer: sender)]
    UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: nil, errorHandler: nil)

    이 코드를 사용해서 우리가 cat detail page를 보고 싶은지, 그리고 detail page에서 보고 싶은 고양이를 결정하는 모든 정보를 포함한 NSUserActivity 를 생성합니다. NSUserActivity 를 구성한 뒤에, 우리는 UIApplication.shared 에 있는 requestSceneSessionActivation(_:, userActivity:, options:, errorHandler:) 함수를 호출하여 새로운 scene을 launch하기위한 request를 생성합니다. 우리가 이 함수를 호출할 때, application(_:, configurationForConnecting:, options:) 가 당신의 AppDelegate 에서 호출됩니다. 이 함수에서는 info.plist 에 있는 configuration과 일치하는 적절한 UISceneConfiguration 을 반환해야합니다. AppDelegate.swift 에 있는 이 함수를 아래와 같이 수정하세요.

    if let activity = options.userActivities.first,
             activity.activityType == "com.donnywals.viewCat" {
       return UISceneConfiguration(name: "Cat Detail",
                                                                 sessionRole: connectingSceneSession.role)
     }
    return UISceneConfiguration(name: "Default Configuration",
                                                         sessionRole: connectingSceneSession.role)

    여기서 우리는 우리가 기대하는 유형의 user activity가 있는지 확인합니다. 만약 존재한다면, 우리의 Cat Detail configuration 인스턴스를 반환하게 됩니다. 만약 기대하는 유형의 유저 activity가 없다면 우리는 Default Configurration 을 반환하게 됩니다. 앱이 작동하기까지가 거의 다 완료되었습니다. 이제 CatSceneDelegate.swift 를 간단하게 살펴봅시다. 아래의 코드를 주목합시다.

    let detail: CatDetailViewController
     if let activity = connectionOptions.userActivities.first,
       let catName = activity.userInfo?["tapped_cat_name"] as? String {
       detail = CatDetailViewController(catName: catName)
     } else {
       detail = CatDetailViewController(catName: "default")
     }

    여기서는 우리에게 유저 activity가 제공이 되었는지 확인합니다. 만약 가지고 있다면, 우리는 연관된 값들을 추출합니다. 만약 실패하거나 유저 activity가 없는 경우, 우리는 default라는 이름으로 CatDetailViewController를 생성합니다. 이 부분이 왜 필요한지 곧 알게 될 것 입니다. 지금 앱을 실행하면, 2마리의 고양이 중 하나를 tap할 때마다 새로운 scene이 생성되는 것을 확인 할 수 있습니다. 지금도 꽤 멋지지만, 둘중 하나가 tap될 때 같은 scene session을 재사용하는게 더 좋을 것 입니다.

     

    이것은 현재 활성된(active) session을 반복하고 각 session에 연결된 targetContentIdentifier 를 검사해서 구현할 수 있습니다. 만약 일치하는 것을 찾으면, 새로운 scene session을 요청하는 것 대신에 특정 scene의 활성화(activation)을 요청할 수 있습니다. didTapCat(_:) 함수에서 아래의 코드를 추가 하세요.

    @objc func didTapCat(_ sender: UITapGestureRecognizer) {
            let activity = NSUserActivity(activityType: "com.donnywals.viewCat")
            activity.targetContentIdentifier = tappedCat(forRecognizer: sender)
            let session = UIApplication.shared.openSessions.first { openSession in
                guard let sessionActivity = openSession.scene?.userActivity,
                      let targetContentIdentifier = sessionActivity.targetContentIdentifier
                else { return false }
    
                return targetContentIdentifier == activity.targetContentIdentifier
            }
            UIApplication.shared.requestSceneSessionActivation(session,
                                                               userActivity: activity,
                                                               options: nil,
                                                               errorHandler: nil)
        }

    Xcode에서 앱을 다시 실행하면, 앱을 종료할 때 활성화된 모든 scene이 다시 생성되는 것을 알아차리게 될 것 입니다. 이것이 이전에 대체할 것을 만든 이유 입니다. 정상적인 앱은 이런 일이 일어나서는 안됩니다. Xcode가 default identifier를 사용하여 기존의 detail scene을 재생성했음에도 불구하고, 같은 고양이를 여러번 tap하면 각 고양이마다 하나의 장면만 열어야 합니다.

     

    유저가 scene을 닫고 싶을 때, iPad의 Expose를 사용할 수도 있습니다. 현재 cat detail page에 닫기 버튼이 있지만 아무것도 구현되어있지 않습니다. 이 부분을 구현하여 사용자가 닫기 버튼을 tap했을 때 현재 scene을 종료하는 코드를 작성해 봅시다. CatDetailViewController.swift 에 있는 close 함수에 아래의 코드를 작성하세요.

    @objc func close() {
        if let session = self.view.window?.windowScene?.session {
            let options = UIWindowSceneDestructionRequestOptions()
            options.windowDismissalAnimation = .commit
            UIApplication.shared.requestSceneSessionDestruction(session,
                                                                options: options,
                                                                errorHandler: nil)
    
        }
    }

    이 코드는 현재 session을 가져오고 UIWindowSceneDestructionRequestOptions 객체를 생성합니다. 우리는 이 객체를 사용하여 scene이 discard될 때 훌륭한 애니메이션을 configuration 할 수 있습니다. 지금의 경우에는 우리는 commit 스타일을 선택합니다. 사용자에게 전달할 의도에 따라 decline , standard 를 선택할 수도 있습니다.

     

    이제 scene을 갱신(refresh)하는 법을 알아봅시다. 이것은 일반적으로 iPad Expose에 있는 앱의 snapshot이 최신 상태이고 사용자 인터페이스를 정확하게 반영하는지 확인하기 위한 작업입니다. 지금 cat 앱은 말이 안되지만, 그렇게 한다고 가정합시다. 다음 코드는 현재 연결된 모든 scene session을 조사해서 만약 session이 관련된 유저 activity를 출력하는데 사용되었다면, 앱에게 session을 갱신하도록 요청합니다.

    for session in UIApplication.shared.openSessions {
        if session.scene?.userActivity?.activityType == "some.activity.type" {   
                    UIApplication.shared.requestSceneSessionRefresh(session)
        }
    }

    갱신 작업이 즉시 수행되지 않을 수 있다는 점에 주의하세요. 시스템은 새로고침을 적절한 시점에 수행할 수(지연) 있는 권한을 가지고 있습니다.

     

    이제 새로운 session을 만들고 같은 content를 위해 재사용, 어떻게 session을 종료하거나 refresh하는지 알아보았습니다. 이제 마지막으로 drag and drop을 사용하여 새로운 scene을 생성하는 방법을 알아봅시다.

    새 scene을 여는 더 나은 방법 추가하기

    우리는 구현하기로한 모든 것을 구현했습니다. 이제 남은 것은 drag and drop 지원 구현입니다. 이 포스트에서는 drag and drop에 대한 모든 것을 다루지는 않습니다. 그 대신에 특정한 시나리오에 대한 drag 인터렉션을 구현하는 방법, drag and drop을 이용하여 새 scene을 여는 방법에 대해서 설명하겠습니다. 구현하기 전에, Info.plist 에서 앱에서 처리할 activity type을 등록해야 합니다. 새로운 Array 에 대한 NSUserActivityTypes key를 추가하고 이 array에 지원하려는 사용자 활동 유형(user activity type)을 추가하세요. 다음 사진과 비슷하게 되어있어야 합니다.

    그다음, CatsOverviewViewController.swift 에서 탭 제스처를 설정하는 코드 직후에 다음 2줄의 코드를 입력하세요.(41 line 근처)

    let dragInteraction = UIDragInteraction(delegate: self)
    image.addInteraction(dragInteraction)

    앞의 코드는 이미지 뷰에 drag 지원을 추가합니다. 다음 단계는 CatsOverviewViewController.swiftUIDragInteractionDelegate 를 채택해서 앱의 새로운 scene을 여는데 사용되는 적절한 drag item을 제공할 수 있도록 하는 것 입니다. CatsOverviewController.swift 의 extension을 아래처럼 추가하세요.

    extension CatsOverviewViewController: UIDragInteractionDelegate {
      func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] {
        let selectedCat = cats[interaction.view?.tag ?? 0]
    
        let userActivity = NSUserActivity(activityType: "com.donnywals.viewCat")
        userActivity.targetContentIdentifier = selectedCat
        userActivity.title = "View Cat"
    
        let itemProvider = NSItemProvider(object: UIImage(named: selectedCat)!)
        itemProvider.registerObject(userActivity, visibility: .all)
    
        let dragItem = UIDragItem(itemProvider: itemProvider)
        dragItem.localObject = selectedCat
    
        return [dragItem]
      }
    }

    이 작업을 한 뒤, 앱을 실행하여 두마리의 고양이 중 하나를 선택해서 화면 옆으로 끌어보세요. 이미지를 drop해서 새로운 floating scene이나 기존 scene의 바로 옆에 scene을 생성할 수 있어야 합니다.

    요약

    다시한번 정리해봅시다. 먼저, 사용자가 multiple scene을 지원하는 앱에서 어떤 것을 기대하는지 알아보았습니다. 그리고 그것들을 어떻게 구성하는지도 알아보았습니다. 그 다음으로 앱이 지원하는 모든 scene과 함께 Info.plist 를 구성하는 방법을 알아보았습니다.

     

    다음으로 사용자가 버튼을 탭할 때 새 scene을 시작하는 방법과 다른 버튼을 탭할 때 scene을 제거하는 방법을 보았습니다. 또한 필요한 경우 scene session을 새로 고치는 방법도 보았습니다. 마지막으로 drag and drop 지원을 추가하여 사용자가 앱의 요소를 화면 측면으로 드래그하여 새로운 장면을 시작할 수 있습니다.

     


     

    원문: Adding support for multiple windows to your iPadOS app

    https://www.donnywals.com/adding-support-for-multiple-windows-to-your-ipados-app/

     

    Adding support for multiple windows to your iPadOS app – Donny Wals

    Learn everything you need about adding multi-window support to your iPad apps. From creating new scenes all the way to reactivating and destroying them.

    www.donnywals.com

    추가 자료: Understanding the iOS 13 Scene Delegate

    https://www.donnywals.com/understanding-the-ios-13-scene-delegate/

     

    Understanding the iOS 13 Scene Delegate – Donny Wals

    New in iOS 13, the scene delegate. But what does it do? And how are you supposed to use it? Learn everything you need to know about the scene delegate in this blog post!

    www.donnywals.com

     

    반응형

    댓글

Designed by Tistory.