관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - SwiftUI] 튜토리얼 - 15. iOS앱 프로젝트에 WatchOS 애플워치 구현 방법 (WatchKit, 공통 사용) 본문

iOS 튜토리얼 (SwiftUI)

[iOS - SwiftUI] 튜토리얼 - 15. iOS앱 프로젝트에 WatchOS 애플워치 구현 방법 (WatchKit, 공통 사용)

jake-kim 2022. 7. 17. 23:54

* 애플워치 타겟 추가 방법은 이전 포스팅 글 먼저 참고

애플워치 앱 뷰 만들기

  • iOS전용으로 만들었던 뷰 LandmarkList에서 셀을 누르면 LandmarkDetail로 이동하는데, 이때 애플워치에서 동일한 파일 이름LandmarkDetail으로 구현할 경우, 애플 워치를 실행했을때 iOS의 LandmarkDetail이 아닌 애플워치가 동작

ex) 애플워치 ContentView에 iOS 전용으로 구현한 LandmarkList를 사용하고, iOS 전용으로 구현한 LandmarkDetail.swift와 동일한 이름으로 애플워치 폴더 하위에 파일을 만들어 실행

  -> LandmarkList는 애플워치에서 구현한게 아니므로 iOS에서 만든 뷰로 동작하고, LandmarkDetail는 애플워치 하위에 동일한 이     름이 존재할때 우선순위가 높으로 애플워치에 구현한 LandmarkDetail이 실행되어 매우 편리하게 사용가능

  • WatchKit Extension 폴더 하위에 LandmarkDetail.swift 파일 생성
  • Targets은 역시 WatchKit Extension 체크

  • SwiftUI로 iOS앱을 만들 때처럼 동일하게 뷰를 구현
import SwiftUI

struct LandmarkDetail: View {
  @EnvironmentObject var modelData: ModelData
  var landmark: Landmark
  
  var landmarkIndex: Int {
    modelData.landmarks.firstIndex(where: { $0.id == landmark.id })!
  }
  
  var body: some View {
    Text("Hello, World!")
  }
}
  • preview도 역시 iOS앱을 만들 때처럼 동일하게 구현
struct LandmarkDetail_Previews: PreviewProvider {
  static var previews: some View {
    let modelData = ModelData()
    return LandmarkDetail(landmark: modelData.landmarks[0])
      .environmentObject(modelData)
  }
}

결과) iOS 앱을 만들때처럼 동일하게 구현하면 애플워치 화면이 등장

  • body 부분에 Hello, World 텍스트 대신 이미지를 삽입

  var body: some View {
    CircleImage(image: landmark.image.resizable())
      .scaledToFill()
  }
  • preview에서 이전 포스팅글에서 알아보았듯이 .previewDevice를 사용하면 애플워치의 다양한 기기 확인이 가능

struct LandmarkDetail_Previews: PreviewProvider {
  static var previews: some View {
    let modelData = ModelData()
    return Group {
      LandmarkDetail(landmark: modelData.landmarks[0])
        .environmentObject(modelData)
        .previewDevice("Apple Watch Series 5 - 44mm")
      
      LandmarkDetail(landmark: modelData.landmarks[1])
        .environmentObject(modelData)
        .previewDevice("Apple Watch Series 5 - 40mm")
    }
  }
}
  • iOS앱을 만들때와 동일하게 body안에 여러 뷰를 추가
  var body: some View {
    VStack {
      CircleImage(image: landmark.image.resizable())
        .scaledToFill()
      
      Text(landmark.name)
        .font(.headline)
        .lineLimit(0)
      
      Toggle(isOn: $modelData.landmarks[landmarkIndex].isFavorite) {
        Text("Favorite")
      }
      
      Divider()
      
      Text(landmark.park)
        .font(.caption)
        .bold()
        .lineLimit(0)
      
      Text(landmark.state)
        .font(.caption)
    }
  }

  • 현재 스크롤이 안되는 상태이고, 이미지 크기가 scaleToFill이라서 영역을 어색하게 차지하고 있으므로 scaleToFit으로 변경
  var body: some View {
    ScrollView { // <-
      VStack {
        CircleImage(image: landmark.image.resizable())
          .scaledToFit() // <-
        
        Text(landmark.name)
          .font(.headline)
          .lineLimit(0)
        
        Toggle(isOn: $modelData.landmarks[landmarkIndex].isFavorite) {
          Text("Favorite")
        }
        
        Divider()
        
        Text(landmark.park)
          .font(.caption)
          .bold()
          .lineLimit(0)
        
        Text(landmark.state)
          .font(.caption)
      }
      .padding(16)
    }
  }

  • 애플워치의 navigationTitle
    • 아래 빨간색으로 감싼 부분이 애플워치의 navigationTitle

  • iOS앱 구현과 동일하게 NavigationView를 감싼 후, 내부 뷰 끝에 navigationTitle을 붙여서 추가
  var body: some View {
    NavigationView { // <-
      ScrollView {
        VStack {
          CircleImage(image: landmark.image.resizable())
            .scaledToFit()
          
          Text(landmark.name)
            .font(.headline)
            .lineLimit(0)
          
          Toggle(isOn: $modelData.landmarks[landmarkIndex].isFavorite) {
            Text("Favorite")
          }
          
          Divider()
          
          Text(landmark.park)
            .font(.caption)
            .bold()
            .lineLimit(0)
          
          Text(landmark.state)
            .font(.caption)
          
          Divider()
          
          MapView(coordinate: landmark.locationCoordinate)
            .scaledToFit()
        }
        .padding(16)
      }
      .navigationTitle("Landmarks") // <-
    }
  }

iOS에 구현된 화면을 애플워치에 사용하기

  • WatchKit Extension 하위에 있는 ContentView 탭

  • iOS앱 뷰에서 구현한 LandmarkList를 그대로 사용
struct ContentView: View {
  var body: some View {
    LandmarkList() // <-
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
      .environmentObject(ModelData())
  }
}

결과) 애플워치에 바로 적용 완료

  • 실행해보면 위에서 구현했던 LandmarkDetail 화면도 적용이 된것을 확인

애플워치 폴더 하위에 구현한&nbsp; LandmarkDetail도 실행

  • 핵심 - iOS앱에서 구현한 파일 이름과 동일하게 애플워치 폴더 하위에 파일을 생성해 놓으면, 애플워치 폴더 하위에 생성한 파일이 실행

* 애플워치에서 커스텀 Notification 방법은 다음 포스팅 글 참고

* 참고

https://developer.apple.com/tutorials/swiftui/creating-a-watchos-app

Comments