스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
UIKit과 SwiftUI 사용
UIKit 앱에서 SwiftUI의 강력한 기능을 활용하는 방법을 알아보세요. UIHostingConfiguration을 사용하여 SwiftUI로 맞춤형 UICollectionView 및 UITableView 셀을 원활하게 빌드해 보세요. 또한 앱 내에서 UIKit와 SwiftUI 구성 요소 간 데이터 흐름을 관리하는 방법을 보여드립니다. 이 세션을 최대한 활용하려면 SwiftUI의 기본 사항을 숙지하시는 것이 좋습니다.
리소스
- Managing model data in your app
- selfSizingInvalidation
- selfSizingInvalidation
- UIHostingConfiguration
- UIHostingController
- UIViewController
- Using SwiftUI with UIKit
관련 비디오
WWDC22
WWDC20
-
다운로드
♪ 부드러운 힙합 음악 ♪ ♪ 안녕하세요, 건강 앱 엔지니어 Sara Frederixon입니다 여러분께 UIKit와 SwiftUI를 쓰는 법을 알려 드릴게요 여러분 대다수처럼 저도 UIKit 앱을 개발해요 제 경우엔 건강 앱이죠 건강 앱은 여러 항목을 시각화해서 사람들이 건강 데이터를 이해할 수 있게 돕죠 그런데 이런 화면의 구현은 상당히 복잡해요 전 SwiftUI를 쓰는 데 관심이 있어서 UIKit을 쓰면서 SwiftUI 팀과도 일했고 하나의 앱에서 둘을 어떻게 통합하는지 배웠어요 이 영상에서 여러분의 UIKit 앱에서 SwiftUI를 사용하는 게 얼마나 쉬운지 알려 드릴게요 우선 UIHostingController를 설명할 거예요 새로 업데이트를 해서 활용도가 더 높아졌죠 다음은 여러분 앱에 이미 존재하는 데이터를 SwiftUI 뷰에 심어 볼 거예요 그리고 데이터가 바뀔 때 SwiftUI 화면 업데이트를 확인할 수 있는지 체크하죠 그다음엔 흥미로운 새 기능을 말씀드릴게요 SwiftUI를 사용해서 UICollectionView와 UITableView 셀을 만드는 겁니다 마지막으로 셀 안에서 SwiftUI를 사용할 때 콜렉션뷰와 테이블뷰에서 볼 수 있는 데이터 흐름의 독특한 관점을 설명할게요 UIHostingController 이야기부터 시작할까요? UIHostingController는 UIViewController인데 SwiftUI 뷰 계층 구조를 포함하고 있는 거예요 호스팅 컨트롤러는 UIKit의 뷰 컨트롤러를 쓰는 데선 전부 쓸 수 있어요 덕분에 UIHostingController는 SwiftUI에 첫발을 들이는 쉬운 방법이죠 호스팅 컨트롤러가 어떻게 작동하는지 살펴보죠 호스팅 컨트롤러는 뷰 컨트롤러예요 즉 뷰 속성에 UIView가 들어 있단 거죠 그 뷰 안에 SwiftUI 콘텐츠가 들어갈 거예요 호스팅 컨트롤러의 사용법을 예시를 통해 알아보죠 여기에 HeartRateView를 SwiftUI 뷰로 만들었어요 HeartRateView를 루트 뷰로 하는 호스팅 컨트롤러를 만들고 표시합니다 UIHostingController는 UIKit 뷰 컨트롤러의 모든 API를 활용할 수 있어요 다른 예시를 보여 드릴게요 아까 만든 HeartRateView와 호스팅 컨트롤러예요 여기에 호스팅 컨트롤러를 추가해 하위 뷰 컨트롤러를 만듭니다 그러면 호스팅 컨트롤러 뷰의 위치와 크기를 조절할 수 있어요 UIHostingController 내부의 SwiftUI 콘텐츠가 바뀌면 뷰의 크기를 재조정해야 할 수도 있어요 iOS 16부터는 UIHostingController에서 뷰 컨트롤러가 권장하는 크기와 뷰 고유의 크기에 대해 자동 업데이트 기능을 사용할 수 있습니다 UIHostingController에서 새로운 크기 설정 속성을 이용하면 가능합니다 다른 예시를 볼까요? 우선 HeartRateView를 만들고 호스팅 컨트롤러를 생성합니다 우리는 새로운 sizingOptions API를 이용해 권장하는 내용 크기를 자동으로 업데이트합니다 그다음 modalPresentationStyle을 팝오버뷰로 설정합니다 새 sizingOptions API를 이용하면 팝오버 뷰의 크기가 언제나 SwiftUI 콘텐츠에 알맞게 설정됩니다 이제 UIHostingController에 익숙해지셨죠? 그러면 UIKit 앱의 다른 부분에서 SwiftUI로 어떻게 데이터를 가져오고 데이터가 바뀔 때마다 SwiftUI 뷰가 업데이트되게 하는 법을 알아볼게요 여러분의 UIKit 앱을 그림으로 표현했어요 기존에 있던 모델 레이어에는 레이어가 소유하고 관리하는 데이터 모델 객체가 있어요 앱에는 여러 개의 뷰 컨트롤러도 있어요 SwiftUI를 쓰고 싶다면 뷰 컨트롤러 중 하나에 SwiftUI 뷰를 포함한 호스팅 컨트롤러가 필요하죠 기존 모델 레이어에 들어 있는 데이터를 이 SwiftUI 뷰에 추가한다고 해 보죠 이번 섹션에서는 UIKit와 SwiftUI의 경계를 넘어 데이터를 연결하는 법을 알아볼게요 SwiftUI는 앱의 데이터를 관리하는 데 도움이 되는 다양한 데이터 흐름 프리미티브를 제공해요 다양한 옵션을 살펴볼까요? SwiftUI 뷰에서 만들고 소유하는 데이터를 저장할 땐 @State 및 @StateObject 속성 래퍼가 제공됩니다 그런데 우리가 주목하는 건 SwiftUI 외부에서 소유한 데이터라 이 속성 래퍼들은 적절하지 않아요 이 부분은 이 영상에서 건너뛰겠습니다 'SwiftUI의 데이터 필수 요소' 영상을 보시면 SwiftUI 뷰가 소유하는 데이터에 대해 배울 수 있어요 SwiftUI 외부의 데이터를 다루는 한 가지 방법은 뷰를 초기화할 때 직접 값을 전달하는 거예요 SwiftUI가 소유하거나 관리하지 않는 원시 데이터만 표시하기 때문에 데이터가 바뀌면 UIHostingController를 수동으로 업데이트해야 해요 예시를 볼게요 여기 HeartRateView라는 SwiftUI 뷰가 있어요 이 뷰는 단일 속성을 갖고 있죠 분당 심박 수를 정수로 저장하고 값을 텍스트로 표시해요 우리는 이 HeartRateView를 UIHostingController에 포함해 표시할 건데요 이 호스팅 컨트롤러는 HeartRateViewController라는 기존의 뷰 컨트롤러 안에 있어요 나중에 루트 뷰를 업데이트할 수 있게 호스트 컨트롤러에 대한 참조를 저장합니다 SwiftUI HeartRateView는 값 타입인 걸 명심하세요 값을 저장하면 별도의 복사본이 생성되고 UI를 업데이트할 수 없어요 HeartRateViewController가 HeartRateView에 들어가는 데이터를 소유하죠 이 데이터는 분당 심박 수 속성에 저장되고 분당 심박 수 값이 바뀌면 뷰를 업데이트하는 메서드를 호출하죠 업데이트 메서드에선 최신 분당 심박 수의 값으로 새로운 심장 박동 뷰를 만든 다음 해당 뷰를 호스팅 컨트롤러의 루트 뷰로 지정해요 UIKit에서 SwiftUI로 데이터를 가져오는 간단한 방법이죠 하지만 데이터가 바뀔 때마다 호스팅 컨트롤러의 루트 뷰를 수동으로 업데이트해야 해요 SwiftUI의 다른 데이터 프리미티브로 이런 업데이트를 자동으로 해 보죠 @ObservedObject와 @EnvironmentObject 속성 래퍼로 ObservableObject 프로토콜을 따르는 외부 모델 객체를 참조할 수 있습니다 이 속성 래퍼를 사용하면 바뀐 데이터를 SwiftUI가 자동으로 업데이트합니다 이 영상에서는 @ObservedObject 속성 래퍼에 집중할 거예요 @EnvironmentObject에 대해 배우고 싶다면 'SwiftUI의 데이터 필수 요소' 영상을 추천해요 @ObservedObject를 만드는 법을 알아볼까요? 첫 번째 단계는 앱의 기존 부분이 소유하고 있던 모델 객체를 가져와 ObservableObject 프로토콜을 준수하게 하는 거예요 그다음 모델을 SwiftUI 뷰에서 @ObservedObject 속성으로 저장해요 SwiftUI에 ObservableObject를 연결하면 속성이 변경될 때 뷰를 업데이트할 수 있어요 HeartRateView 예시로 돌아가서 종합해 볼게요 우리 앱은 HeartData라는 클래스가 있어요 여기 포함된 속성 중에 분당 심박 수가 있죠 우리는 프로토콜을 준수하여 ObservableObject를 만들고 분당 심박 수 속성에 @Published 속성 래퍼를 추가했어요 이 속성 래퍼가 SwiftUI 뷰에 변경 사항을 업데이트하게 하죠 우리는 HeartRateView에서 HeartData를 @ObservedObject 속성 래퍼가 달린 속성에 저장합니다 뷰의 본문에서 HeartData에 직접 분당 심박 수를 표시했죠 이제 뷰 컨트롤러에서 함께 사용해 볼까요? HeartRateView 컨트롤러예요 속성에 관찰 가능한 객체 Heartdata를 저장하죠 이 속성은 SwiftUI 뷰 내부에 있는 게 아니라 여기서 속성 래퍼를 사용할 필요가 없어요 HeartRateViewController는 HeartData 인스턴스로 초기화하죠 이건 호스팅 컨트롤러의 루트 뷰가 되는 HeartRateView를 생성하는 데 사용돼요 어떻게 합쳐지는지 도식으로 보여 드릴게요 현재의 HeartData 인스턴스를 불러와서 분당 78회 심박수라는 수치가 포함돼 있어요 HeartData 인스턴스로 새 HeartRateViewController를 생성해요 SwiftUI HeartRateView와 묶이는 거죠 몇 초가 지나서 새로운 심박 수 데이터 샘플이 도착하면 HeartData의 심박 수 속성이 94로 업데이트되죠 ObservableObject의 게시된 속성이 변경되었기 때문에 HeartRateView가 자동으로 업데이트돼서 새 값이 표시됩니다 이젠 데이터가 바뀔 때마다 호스팅 컨트롤러를 수동으로 업데이트할 필요 없어요 이래서 ObservableObject가 UIKit에서 SwiftUI로의 훌륭한 데이터 연결 방법인 거죠 이제 SwiftUI를 컬렉션 뷰와 테이블 뷰 셀에서 사용하는 방법을 얘기해 보죠 iOS 16의 새로운 기능인 UIHostingConfiguration은 기존의 UIKit과 컬렉션 뷰, 테이블 뷰 안에서 SwiftUI의 기능을 활용할 수 있게 하는 도구죠 UIHostingConfiguration은 SwiftUI를 사용해 맞춤 셀을 쉽게 구현할 수 있어요 추가로 뷰나 뷰 컨트롤러를 포함할 필요가 없죠 UIHostingConfiguration을 자세히 들여다보기 전에 UIKit의 셀 구성을 살펴볼게요 셀 구성은 UIKit에서 셀의 내용과 스타일 및 동작을 현대적으로 정의하는 방법입니다 UIView나 UIViewController와 달리 셀 구성은 가벼운 구조체입니다 그만큼 만들기가 쉬워요 셀 구성은 셀의 모습에 대한 설명일 뿐이라 셀에 적용이 되어야 효과를 볼 수 있습니다 셀 구성은 중첩이 가능하고 UICollectionView와 UITableView 셀에 전부 적용 가능합니다 자세한 내용은 '현대 셀 구성'을 시청하세요 이 복습을 염두에 두고 UIHostingConfiguration을 사용해 보겠습니다 UIHostingConfiguration은 SwiftUI View Builder로 초기화한 콘텐츠 구성입니다 즉 SwiftUI 코드를 작성해서 내부에 바로 뷰를 만들 수 있다는 거죠 호스팅 구성을 제시하기 위해 UICollectionView나 UITableView 셀의 contentConfiguration 속성로 설정합니다 이 호스팅 설정에서 SwiftUI 코드를 써서 심장 박동 셀을 만들어 봐요 '심장 박동'이라는 텍스트와 하트 이미지가 들어간 이름표부터 만들어 볼게요 Swift UI 뷰는 자신이 들어 있는 컨텍스트의 기본적인 스타일을 따라가요 하지만 우리는 스타일을 따로 설정해 보죠 일반적인 SwiftUI 뷰 수정자를 사용해서요 우리 이름표에 전경 스타일과 서체 수정자와 더해서 이미지와 텍스트를 분홍으로 하고 글씨체를 굵게 할게요 일반적인 SwiftUI 코드를 사용하기 때문에 우리가 원할 때는 언제든 독립된 뷰에서만 코드를 뽑아 낼 수 있어요 자, 새로운 SwiftUI 뷰를 만들게요 이름은 HeartRateTitleView예요 아까 그 코드는 새 뷰의 본문으로 옮기고 HeartRateTitleView를 호스팅 구성에 넣을게요 셀에서 보이듯이 똑같은 결과가 나와요 이제 HeartRateTitleView의 내부에 다른 뷰들을 넣을 수 있어요 H스택에 공백으로 된 새로운 이름표를 넣어 볼게요 그 옆에 현재 시각을 적은 텍스트 뷰를 추가하는 거죠 지금까지는 순조롭네요 이 셀의 HeartRateTitleView 아래에 내용을 더 많이 추가해 보죠 그러려면 호스팅 구성 안에 V스택을 넣어야겠어요 그래야 HeartRateTitleView 아래에 내용을 추가할 수 있죠 그리고 텍스트 뷰 두 개를 H스택으로 묶어서 넣어요 90BPM을 표시하는 뷰죠 몇 가지 수정자로 원하는 스타일을 적용합니다 HeartRateTitleView에서 했던 것처럼요 이 새로운 코드를 따로 새 SwiftUI 뷰로 옮길 수도 있어요 같은 코드를 본문에서 잘라 내서 HeartRateBPMView를 만들었어요 근사한 셀이 됐네요 그런데 다른 걸 더해 보면 좋겠어요 iOS 16의 신기능 Swift Charts 프레임워크는 데이터를 예쁜 그래프로 시각화하는 데 코드 몇 줄로 끝나요 이제 이 기능을 활용해서 작은 꺾은선 차트를 셀 안에 표시해 볼게요 새로운 차트 뷰를 이용해서 작은 꺾은선 차트를 만들어 최근 심장 박동 샘플을 시각화하고 셀의 BPM 뷰 옆에 표시했습니다 차트를 만들기 위해 심장 박동 샘플 컬렉션을 가져왔어요 이 샘플과 연결되는 LineMark를 그렸죠 원 기호를 더해서 꺾은선에 각 샘플을 표시하고 분홍색 전경 스타일을 적용했어요 HeartRateTitleViw와 차트 색깔을 맞추려고요 새로운 Swift Charts 프레임워크로 할 수 있는 것들에 비하면 이건 겨우 맛보기예요 '안녕, Swift Charts' 영상을 꼭 보고 더 자세히 배워 보세요 의심장 박동 셀이 근사하게 마무리됐을 뿐 아니라 몇 분 만에 쉽게 만들었어요 UIHostingConfiguration과 SwiftUI를 이용해서 맞춤 셀을 만드는 건 이렇게 쉽답니다 이제 UIHostingConfiguration이 지원하는 네 가지 특별한 기능을 얘기해 볼까요? 기본적으로 루트 단계의 SwiftUI 콘텐츠는 셀 가장자리에서부터 UIKit의 셀 레이아웃 여백을 기준으로 삽입됩니다 이러면 셀의 내용이 인접한 셀 및 탐색 바 같은 다른 UI 요소와 알맞게 정렬되죠 가끔은 다른 여백을 사용하거나 내용이 셀 가장자리까지 확장되게 할 수도 있어요 그럴 땐 UIHostingConfiguration에서 여백 수정자를 사용해 기본 여백을 변경하면 됩니다 SwiftUI를 사용해서 셀의 배경을 바꾸고 싶으면 UIHostingConfiguration의 배경 수정자를 사용하면 됩니다 UIHostingConfiguration의 배경과 콘텐츠 사이에는 몇 가지 중요한 차이가 있습니다 배경은 셀의 뒤쪽에 자리를 잡습니다 셀의 콘텐츠 뷰에서 SwiftUI 콘텐츠 아래 깔리는 거죠 또 콘텐츠는 일반적으로 셀의 가장자리에서부터 삽입되는데 배경은 가장자리에서부터 가장자리까지 쭉 펼쳐져 있죠 마지막으로 크기를 스스로 조절하는 셀의 경우엔 셀의 콘텐츠만 크기에 영향을 미칩니다 이제 UIHostingConfiguration의 특별한 기능을 두 가지만 더 말해 볼게요 여러분이 컬렉션 뷰 목록이나 테이블 뷰 안에 셀이 있을 때 활용할 수 있을 겁니다 목록에서 셀 아래 구분 기호는 기본적으로 호스팅 구성의 SwiftUI 텍스트와 자동 정렬 됩니다 이 예시에서도 구분 기호의 왼쪽 시작 지점이 셀의 텍스트와 정렬되기 때문에 이미지를 지나서부터 시작되고 있죠 호스팅 구성의 다른 SwiftUI 뷰에 맞춰 구분 기호를 정렬해야 한다면 alignmentGuide 수정자를 사용하세요 컬렉션 뷰 목록이나 테이블 뷰에선 SwiftUI를 사용해 행에 대한 스와이프 동작을 직접 구성할 수 있습니다 swipeActions 수정자 안에 버튼을 만들면 셀을 스와이프하여 사용자 지정 동작을 표시하고 수행할 수 있습니다 영상의 샘플 코드를 다운로드해서 전체 예시를 확인해 보세요 스와이프 동작을 정의할 때 표시되는 항목에 대한 안정적인 식별자를 사용해서 버튼이 동작을 수행하는지 확인하세요 인덱스 경로는 사용하지 마세요 셀이 보이는 동안 바뀌어서 잘못된 항목에 스와이프 동작이 수행될 수 있습니다 UIHostingConfiguration을 셀에서 사용할 때 탭 처리, 강조 표시, 선택 같은 셀 상호 작용은 컬렉션 뷰나 테이블 뷰에서 여전히 처리된다는 걸 명심하세요 UIKit의 셀 상태와 상관없이 SwiftUI 뷰를 따로 바꾸고 싶다면 셀의 configurationUpdateHandler 안에 호스팅 구성을 만들고 SwiftUI 코드에서 제공하는 상태를 사용할 수 있습니다 configurationUpdateHandler는 셀의 상태가 변할 때마다 다시 실행되고 새로운 상태에 대한 UIHostingConfiguration을 만들어 셀에 적용합니다 이 예시에서는 상태를 활용해 셀이 선택될 때 체크 기호 이미지를 추가했어요 이제 UIHostingConfiguration에 익숙해지셨으니 모델 레이어에서의 데이터 흐름을 관리하는 법을 얘기해 볼까요? 데이터의 목적지는 SwiftUI 셀로 채워진 UIKit의 컬렉션 뷰나 테이블 뷰가 되겠죠 목표는 이 건강 상태 목록을 완성하는 거예요 이 예시에서는 UICollectionView를 사용하지만 우리가 얘기하는 건 모두 UITableView에도 똑같이 적용돼요 관련 구성 요소를 살펴볼까요? 앱에는 MedicalCondition 모델 객체의 컬렉션이 있어요 우리가 컬렉션 뷰로 표시할 내용이죠 이 컬렉션의 각 항목에 대해 컬렉션 뷰의 셀을 생성해서 건강 상태를 표시하려고 해요 그러려면 컬렉션 뷰에 연결된 가변 데이터 원본을 만들어야겠죠 그다음 가변 데이터 원본의 스냅샷을 더해야 하는데요 여기엔 데이터 컬렉션에 있는 MedicalCondition 모델 객체의 식별자를 포함해야 해요 가변 데이터 원본의 스냅샷이 포함하고 있는 게 MedicalCondition 객체 자체가 아니라 MedicalCondition 각각의 고유 식별자인 게 중요해요 이렇게 하면 가변 데이터 원본이 각 항목이 식별자를 정확하게 추적하고 나중에 새 스냅샷을 적용할 때 변경 사항을 올바르게 계산할 수 있죠 항목 식별자를 포함한 스냅샷을 가변 데이터 원본에 적용하면 자동으로 컬렉션 뷰에 업데이트될 거예요 항목마다 새로운 셀이 생성되겠죠 각 셀은 UIHostingConfiguration의 SwiftUI 뷰를 사용해 하나의 MedicalCondition을 표시하도록 구성됩니다 이제 SwiftUI로 만든 셀이 표시됐으니 데이터가 변했을 때 UI 업데이트를 처리해야죠 변경 사항에는 두 가지 유형이 있고 이 두 가지를 별도로 처리해야 합니다 데이터 컬렉션 자체가 바뀌는 게 첫 번째 유형이에요 예를 들어 항목이 삽입되거나 순서가 바뀌고 삭제되는 거죠 이런 변경 사항은 가변 데이터 원본의 새 스냅샷을 적용해 처리합니다 가변 데이터 원본은 이전과 새 스냅샷을 비교하고 컬렉션 뷰에 필요한 업데이트를 수행해서 셀을 삽입하거나 이동, 삭제할 겁니다 데이터 컬렉션 자체의 변화는 셀 내부에는 영향을 미치지 않아서 셀을 UIKit로 만들든 SwiftUI로 만들든 이런 유형의 변경 사항은 똑같이 처리해요 우리가 처리할 두 번째 유형의 변경 사항은 개별 모델 객체의 속성이 바뀌는 경우에요 보통 이런 변경 사항은 셀의 뷰를 업데이트해야 하죠 가변 데이터 원본은 스냅샷에 항목 식별자만 포함해서 기존 항목의 속성이 언제 변경되는지 몰라요 기존에 UIKit을 사용할 때는 가변 데이터 원본에 스냅샷의 항목을 재구성하거나 다시 로딩하면서 수동으로 변경 사항을 알려야 했는데요 셀에서 SwiftUI를 사용하면서 그럴 필요가 없어졌어요 SwiftUI 뷰의 ObservableObject 속성에 관찰 가능한 모델을 저장하면서 모델의 게시된 속성이 바뀌면 자동으로 SwiftUI를 작동시켜 뷰를 새로 고칩니다 즉 셀 내부의 SwiftUI와 모델이 직접적으로 연결되죠 변경 사항이 생기면 셀 안의 SwiftUI가 바로 업데이트합니다 가변 데이터 소스나 UICollectionView를 거치지 않는 거죠 셀의 데이터가 바뀌면 셀이 새로운 내용에 맞게 커지거나 작아질 수 있습니다 하지만 SwiftUI 셀의 내용이 UIKit를 거치지 않고 바로 업데이트가 된다면 컬렉션 뷰가 어떻게 셀의 크기를 재조정할까요? UIHostingConfiguration은 UIKit의 새 기능을 활용하여 이 작업을 수행합니다 컬렉션 뷰와 테이블 뷰에서 자체 크기 조정이 되던 셀들이 iOS 16에선 재조정도 자동으로 가능합니다 기본 기능이므로 UIHostingConfiguration을 사용하면서 SwiftUI 콘텐츠가 바뀌었을 때 필요한 경우 해당 셀 크기가 자동으로 재조정됩니다 이 기능에 대해 자세히 알고 싶으시면 WWDC 2022의 'UIKit 새 소식' 영상을 보세요 여러분이 다뤄야 할 데이터 흐름의 방식이 한 가지 더 있는데요 SwiftUI 뷰의 데이터를 앱의 다른 부분으로 다시 전송하는 겁니다 이번에도 ObservableObject가 해결해 줄 거예요 관찰 가능한 객체의 게시된 속성에 대해 양방향 바인딩을 생성할 수 있어요 데이터 흐름이 객체에서 SwiftUI로 가는 것뿐 아니라 SwiftUI에서 모델 객체의 속성을 변경할 수도 있죠 MedicalCondition 셀의 텍스트를 편집 가능하게 만들어서 양방향 바인딩을 만드는 간단한 예를 살펴볼게요 우리의 ObservableObject MedicalCondition입니다 ID 속성에 고유 식별자가 저장돼 있죠 가변 데이터 원본의 스냅샷에 포함되는 식별자입니다 이 게시된 속성은 건강 상태의 텍스트를 저장합니다 여기 MedicalConditionView은 각각의 셀 안에서 건강 상태를 표시하죠 지금은 읽기 전용이니까 편집이 가능하도록 만들어 보죠 여기서 텍스트 뷰를 TextField로 바꾸고 MedicalCondition의 텍스트 속성에 달러 기호를 앞에 추가해 바인딩을 만들면 됩니다 텍스트 필드에 타이핑을 하면 이 바인딩을 통해 SwiftUI가 변경된 내용을 ObservableObject에 직접 반영합니다 SwiftUI를 이용하면 양방향 데이터 흐름을 설정하는 것도 간단하죠? UIHostingController는 SwiftUI 콘텐츠를 UIKit 앱에 넣을 수 있는 강력한 방법입니다 여러분의 SwiftUI 뷰는 호스팅 컨트롤러 뷰 안에 만들어지고 호스팅 컨트롤러의 사용은 UIKit가 뷰 컨트롤러를 쓰는 모든 위치에서 가능합니다 UIHostingController를 사용할 때는 앱에 뷰와 함께 뷰 컨트롤러를 항상 추가해야 하는 거 잊지 마세요 툴바나 키보드 단축키 및 UIViewControllerRepresentable을 쓰는 뷰와 같은 SwiftUI 기능은 UIKit의 뷰 컨트롤러 계층 구조에 연결되어야 제대로 통합될 수 있습니다 그러니까 호스팅 컨트롤러의 뷰를 호스팅 컨트롤러 자체에서 분리하지 마세요 비교를 해 볼게요 UIHostingConfiguration을 셀에 적용하면 UIViewController 없이 SwiftUI 뷰가 호스트됩니다 UIHostingConfiguration은 SwiftUI 기능의 대부분을 지원합니다 그런데 주의할 것은 UIViewControllerRepresentable에 종속된 SwiftUI 뷰를 셀 내에서 사용할 수 없다는 거죠 UIHostingController와 UIHostingConfiguration이라는 두 가지 훌륭한 방법이 있다면 UIKit 앱에 SwiftUI를 통합할 수 있습니다 SwiftUI는 기존의 UIKit 앱에 원활하게 통합됩니다 UIHostingController를 사용해 앱에 SwiftUI를 추가해 보세요 UIHostingConfiguration을 사용해서 컬렉션 뷰와 테이블 뷰에 맞춤 셀을 만들어 보세요 ObservableObject를 활용하면 여러분의 데이터와 UI는 항상 동기화됩니다 오늘 여러분의 앱에 SwiftUI를 넣어 보세요 봐 주셔서 감사합니다 ♪
-
-
2:09 - Presenting a UIHostingController
// Presenting a UIHostingController let heartRateView = HeartRateView() // a SwiftUI view let hostingController = UIHostingController(rootView: heartRateView) // Present the hosting controller modally self.present(hostingController, animated: true)
-
2:31 - Embedding a UIHostingController
// Embedding a UIHostingController let heartRateView = HeartRateView() // a SwiftUI view let hostingController = UIHostingController(rootView: heartRateView) // Add the hosting controller as a child view controller self.addChild(hostingController) self.view.addSubview(hostingController.view) hostingController.didMove(toParent: self) // Now position & size the hosting controller’s view as desired…
-
3:13 - Presenting UIHostingController as a popover
// Presenting UIHostingController as a popover let heartRateView = HeartRateView() // a SwiftUI view let hostingController = UIHostingController(rootView: heartRateView) // Enable automatic preferredContentSize updates on the hosting controller hostingController.sizingOptions = .preferredContentSize hostingController.modalPresentationStyle = .popover self.present(hostingController, animated: true)
-
5:27 - Passing data to SwiftUI with manual UIHostingController updates
// Passing data to SwiftUI with manual UIHostingController updates struct HeartRateView: View { var beatsPerMinute: Int var body: some View { Text("\(beatsPerMinute) BPM") } } class HeartRateViewController: UIViewController { let hostingController: UIHostingController< HeartRateView > var beatsPerMinute: Int { didSet { update() } } func update() { hostingController.rootView = HeartRateView(beatsPerMinute: beatsPerMinute) } }
-
7:51 - Passing an ObservableObject to automatically update SwiftUI views
// Passing an ObservableObject to automatically update SwiftUI views class HeartData: ObservableObject { @Published var beatsPerMinute: Int init(beatsPerMinute: Int) { self.beatsPerMinute = beatsPerMinute } } struct HeartRateView: View { @ObservedObject var data: HeartData var body: some View { Text("\(data.beatsPerMinute) BPM") } }
-
8:30 - Passing an ObservableObject to automatically update SwiftUI views
// Passing an ObservableObject to automatically update SwiftUI views class HeartRateViewController: UIViewController { let data: HeartData let hostingController: UIHostingController<HeartRateView> init(data: HeartData) { self.data = data let heartRateView = HeartRateView(data: data) self.hostingController = UIHostingController(rootView: heartRateView) } }
-
9:52 - UIHostingConfiguration
cell.contentConfiguration = UIHostingConfiguration { // Start writing SwiftUI here! }
-
11:02 - Building a custom cell using SwiftUI with UIHostingConfiguration
// Building a custom cell using SwiftUI with UIHostingConfiguration cell.contentConfiguration = UIHostingConfiguration { HeartRateTitleView() } struct HeartRateTitleView: View { var body: some View { HStack { Label("Heart Rate", systemImage: "heart.fill") .foregroundStyle(.pink) .font(.system(.subheadline, weight: .bold)) Spacer() Text(Date(), style: .time) .foregroundStyle(.secondary) .font(.footnote) } } }
-
12:46 - Building a custom cell using SwiftUI with UIHostingConfiguration
// Building a custom cell using SwiftUI with UIHostingConfiguration cell.contentConfiguration = UIHostingConfiguration { VStack(alignment: .leading) { HeartRateTitleView() Spacer() HeartRateBPMView() } } struct HeartRateBPMView: View { var body: some View { HStack(alignment: .firstTextBaseline) { Text("90") .font(.system(.title, weight: .semibold)) Text("BPM") .foregroundStyle(.secondary) .font(.system(.subheadline, weight: .bold)) } } }
-
13:41 - Building a custom cell using SwiftUI with UIHostingConfiguration, with a chart!
// Building a custom cell using SwiftUI with UIHostingConfiguration cell.contentConfiguration = UIHostingConfiguration { VStack(alignment: .leading) { HeartRateTitleView() Spacer() HStack(alignment: .bottom) { HeartRateBPMView() Spacer() Chart(heartRateSamples) { sample in LineMark(x: .value("Time", sample.time), y: .value("BPM", sample.beatsPerMinute)) .symbol(Circle().strokeBorder(lineWidth: 2)) .foregroundStyle(.pink) } } } }
-
14:41 - Content margins
cell.contentConfiguration = UIHostingConfiguration { HeartRateBPMView() } .margins(.horizontal, 16)
-
15:16 - Cell backgrounds
cell.contentConfiguration = UIHostingConfiguration { HeartTitleView() } .background(.pink)
-
16:32 - List swipe actions
cell.contentConfiguration = UIHostingConfiguration { MedicalConditionView() .swipeActions(edge: .trailing) { … } }
-
17:25 - Incorporating UIKit cell states
// Incorporating UIKit cell states cell.configurationUpdateHandler = { cell, state in cell.contentConfiguration = UIHostingConfiguration { HStack { HealthCategoryView() Spacer() if state.isSelected { Image(systemName: "checkmark") } } } }
-
23:17 - Creating a two-way binding to data in SwiftUI
// Creating a two-way binding to data in SwiftUI class MedicalCondition: Identifiable, ObservableObject { let id: UUID @Published var text: String } struct MedicalConditionView: View { @ObservedObject var condition: MedicalCondition var body: some View { HStack { Spacer() } } }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.