ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • View Snapshot Capture
    iOS 2024. 6. 18. 07:20

    안녕하세요. 그린입니다 🍏

    이번 포스팅에서는 뷰 스냅샷을 캡쳐하는 방법에 대해 한번 구현해볼까 합니다 🙋🏻

     

    우선 크게 두가지 방법이 존재해요.

    하나는, 현재 나타난 전체 화면의 스냅샷을 따서 캡쳐하는 방법과 또 하나는, 원하는 뷰 영역만 캡쳐하는 방법입니다.

     

    그럼 한번 바로 알아볼까요?

     


    전체 화면 캡쳐하기

    우선 코드부터 볼까요?

     

    func takeSnapshot() async throws -> UIImage {
      return try await MainActor.run {
        var totalImage: UIImage?
        
        guard let keyWindow = UIApplication.shared.connectedScenes
          .compactMap({ $0 as? UIWindowScene })
          .flatMap({ $0.windows })
          .first(where: { $0.isKeyWindow }) else {
          throw NSError(domain: "SnapshotError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to get key window layer"])
        }
        
        guard let currentLayer = keyWindow.layer else {
          throw NSError(domain: "SnapshotError", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to get key window layer"])
        }
        
        let renderer = UIGraphicsImageRenderer(size: currentLayer.frame.size, format: UIGraphicsImageRendererFormat.default())
        totalImage = renderer.image { context in
          currentLayer.render(in: context.cgContext)
        }
        
        return totalImage ?? UIImage()
      }
    }

     

    간단히 메서드로 적용해봤습니다.

    반환 타입은 캡쳐된 뷰를 UIImage로 만들어서 필요한 부분에서 자유롭게 사용할 수 있도록 만들었습니다.

     

    먼저 UI 관련 작업이니 메인 스레드에서 실행될 수 있도록 MainActor에서 실행합니다.

     

    그리고, totalImage는 전체 뷰 스냅샷 이미지를 저장할 변수로 선언해요.

     

    현재 연결된 씬들을 순회하면서 UIWindowScene으로 캐스팅하여 모든 윈도우를 가져오고 그 중에 key Window를 찾습니다.

    만약 찾지 못한다면 적절히 에러를 만들어서 반환하면 됩니다.

     

    그리고 반환된 키 윈도우의 레이어를 가져오는데, 이때도 가져올 수 없다면 적절히 여러분들이 만든 에러를 반환하면 됩니다.

     

    마지막으로, 키 윈도우의 레이어 크기에 맞는 UIGraphicsImageRenderer를 실행하여 이미지를 만듭니다.

    렌더링 컨텍스트 내에서 키 윈도우의 레이어를 그린다고 볼 수 있어요.

     

    그리고 마지막으로 생성된 이미지를 반환하면 됩니다.

     

    이렇게 단순한 로직으로 어디서든 호출하여 현재 씬으로부터 전체 뷰를 찾아 가져와서 렌더링을 통해 이미지 데이터로 반환해 사용할 수 있겠죠?

     

    그럼 이번엔 전체 스냅샷이 아닌 원하는 부분에서의 뷰만 스냅샷을 캡쳐해서 UIImage로 사용하는 방법을 보겠습니다.

     

    SwiftUI 환경에서 해볼께요!


    특정 뷰 캡쳐하기

    이것도 우선 코드로 볼까요?

     

    public extension View {
      @MainActor
      func captureView(
        of view: some View,
        scale: CGFloat = 1.0,
        size: CGSize? = nil,
        completion: @escaping (UIImage?) -> Void
      ) {
        let renderer = ImageRenderer(content: view)
        renderer.scale = scale
        
        if let size = size {
          renderer.proposedSize = .init(size)
        }
        
        completion(renderer.uiImage)
      }
    }

     

    우선 View 프로토콜을 확장해 편하게 메서드로 만들어봤습니다.

    현재 캡쳐할 뷰가 of 레이블 인자로 들어옵니다.

    그리고, 적절히 스케일과 사이즈를 지정해줘도 되고 현재 그대로 보여주겠다면 기본 값처럼 주어도 됩니다.

    그리고 컴플리션 핸들러로 반환된 UIImage를 가지고 작업할 로직을 넣어주면 되죠!

     

    ImageRenderer 객체를 사용해 캡쳐할 뷰를 넣어줍니다.

    그리고 스케일과 사이즈에 대해 적용하는 로직을 넣고 마지막으로 그렇게 적용된 renderer의 uiImage를 사용해 컴플리션에 넘겨주는 간단한 코드입니다.

     

    실제 적용해볼까요?

     

    struct ParentView: View {
      @State private var capturedImage: UIImage?
      
      var body: some View {
        VStack {
          ChildView()
          
          Button("Capture View") {
            captureView(of: ChildView()) { image in
              capturedImage = image
            }
          }
          .padding()
          
          if let capturedImage = capturedImage {
            Image(uiImage: capturedImage)
              .resizable()
              .scaledToFit()
              .frame(width: 200, height: 200)
              .border(Color.black)
          }
        }
        .padding()
      }
    }
    
    private struct ChildView: View {
      var body: some View {
        Text("Green")
          .font(.largeTitle)
          .padding()
          .background(Color.green)
      }
    }

     

    이렇게 뷰 모디파이어를 활용해 of에 원하는 뷰를 넣어 자유롭게 캡쳐해서 사용할 수 있습니다.

    물론, 해당 뷰 모디파이어의 반환 값을 View로 하여 해도 마찬가지로 잘 적용할 수 있습니다ㅎㅎ

     

    편한걸로 적절히 구현하면되고 핵심은 로직적인 부분이라고 보면 될 것 같아요!

     


    마무리

    특정 뷰 및 전체 뷰를 캡쳐하여 공유 기능을 사용해 공유하는것은 빈번히 일어나기에 알아두면 좋을 부분을 이참에 해봤습니다 😃

     


    레퍼런스

     

    ImageRenderer | Apple Developer Documentation

    An object that creates images from SwiftUI views.

    developer.apple.com

     

    UIGraphicsImageRenderer | Apple Developer Documentation

    A graphics renderer for creating Core Graphics-backed images.

    developer.apple.com

    'iOS' 카테고리의 다른 글

    Get started with Dynamic Type (feat. WWDC 2024)  (11) 2024.08.26
    UIScrollView의 contentInsetAdjustmentBehavior  (76) 2024.07.18
    Haptic Feedback  (58) 2024.06.11
    UIPasteboard를 통한 클립보드 사용하기  (66) 2024.05.07
    Core Transferable 톺아보기  (62) 2024.04.18
Designed by Tistory.