SwiftUI is an innovative, exceptionally simple way to build user interfaces across all Apple platforms with the power of Swift. Build user interfaces for any Apple device using just one set of tools and APIs.
Xcode-SwiftUI-Apple Developer
SwiftUI是一种创新的,非常简单的方法,可以借助Swift为Apple全平台构建用户界面。 仅使用一组工具和API就能为任何Apple设备构建用户界面。尽管SwiftUI的工具有限,但配合UIView和UIViewControoler的使用能弥补这一缺憾。原文|地址
本文将通过几个在项目开发中实际用到的例子来介绍
- 如何在SwiftUI view中集成
UIView和UIViewControoler - 如何在SwfitUI和UIKit中传递数据
在SwiftUI中使用UIView
将UIView集成到SwiftUI中的过程主要分为两步:
- 声明一个符合SwiftUI view的
UIViewRepresentable - 在1中声明的
struct中实现两种必须的方法makeUIView和updateUIView我们这里以一个视频播放器View为例,代码如下: import UIKit import SwiftUI
// 声明PlayerView代表PlayerUIView struct PlayerView: UIViewRepresentable {
// update方法使我们能够将UIView和SwiftUI状态更新保持同步,目前我们暂且将其保留为空
func updateUIView(_ uiView: PlayerUIView, context: Context) {
}
// make方法返回初始视图
func makeUIView(context: Context) -> PlayerUIView {
}
}
下面给出PlayerUIView的代码
class PlayerUIView: UIView {
private let playerLayer = AVPlayerLayer()
var player = AVPlayer()
override init(frame: CGRect) {
super.init(frame: frame)
let mediaPath = Bundle.main.path(forResource: "Cossack Squat", ofType: "mp4")
let mediaURL = URL(fileURLWithPath: mediaPath!)
player = AVPlayer(url:mediaURL as URL)
player.actionAtItemEnd = .none
playerLayer.player = player
NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd(notification:)), name: .AVPlayerItemDidPlayToEndTime,
object: player.currentItem)
layer.addSublayer(playerLayer)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
playerLayer.frame = bounds
}
@objc func playerItemDidReachEnd(notification: Notification) {
if let playerItem = notification.object as? AVPlayerItem {
playerItem.seek(to: .zero, completionHandler: nil)
}
}
func beginPlayVideo() {
player.play()
}
func stopPlayVideo() {
player.pause()
}
}
这里希望通过SwiftUI中的变量来控制PlayerUIView中视频的播放与暂停,因此SwifUI的代码为:
struct ContentView: View {
@State var isBegin = false
@State var isFinished = false
var body: some View {
PlayerView(isBegin: $isBegin, isFinished: $isFinished)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 220)
}
}
同时修改PlayerView中的代码为:
// video player
struct PlayerView: UIViewRepresentable {
@Binding var isBegin: Bool
@Binding var isFinished: Bool
func updateUIView(_ uiView: PlayerUIView, context: Context) {
if isBegin {
uiView.beginPlayVideo()
}
if isFinished {
uiView.stopPlayVideo()
}
}
func makeUIView(context: Context) -> PlayerUIView {
return PlayerUIView(frame: .zero)
}
}
通过以上的例子便能将UIView与SwiftUI连接,并通过Binding传递数据达到SwiftUI控制UIView的目的
在SwiftUI中使用UIViewController
UIViewController集成的过程几乎与UIView相同。即SwiftUI视图必须符合UIViewControllerRepresentable协议并实现相同的方法集。
// Need UIViewControllerRepresentable to show any UIViewController in SwiftUI
struct CameraView : UIViewControllerRepresentable {
// Init your ViewController
func makeUIViewController(context: Context) -> JointViewController {
let controller = JointViewController()
return controller
}
// Tbh no idea what to do here
func updateUIViewController(_ uiViewController: JointViewController, context: Context) {
}
}
如果希望将SwiftUI中的数据传入UIViewController则是相同的操作,通过Binding然后传入update方法中. 那么怎样将UIViewController中处理完的数据再次返回SwiftUI呢?这里就需要用到Coordinator. 通过委托,目标动作,回调和KVO与UIKit通信是Coordinator的责任:
extension CameraView {
class Coordinator: NSObject, CameraViewDelegate {
@Binding var isFinished: Bool
@Binding var currentNum: Int
@Binding var desiredGoal: Int
init(isFinished: Binding<Bool>, currentNum: Binding<Int>, desiredGoal: Binding<Int>) {
_isFinished = isFinished
_currentNum = currentNum
_desiredGoal = desiredGoal
}
func CameraViewDidFinished(_ viewController: JointViewController) {
isFinished = viewController.finishFlag
}
func OneGroupDidFinished(_ viewController: JointViewController) {
currentNum = viewController.successCount
}
func ConfirmDesiredGoal(_ viewController: JointViewController) {
viewController.desiredGoal = desiredGoal
}
}
}
我们先不用理会这三个数据具体的意思,只需要知道isFinished和currentNum是UIViewController希望通过Coordinator传递给我SwiftUI的,而desiredGoal是SwiftUI希望在改变其值后传入到UIViewController中
下面给出JointViewController相关代码
class JointViewController: UIViewController {
var delegate : CameraViewDelegate?
// number of desired amount
public var desiredGoal: Int = 0
public var finishFlag = false
public var successCount = 0
// MARK: - View Controller Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
// setup delegate for performance measurement
👨🔧.delegate = self
}
func compareWithStandardPoints() {
...
} else {
delegate?.ConfirmDesiredGoal(self)
successCount += 1
print("Success count: ", successCount)
delegate?.OneGroupDidFinished(self)
}
}
}
protocol CameraViewDelegate: NSObjectProtocol {
func CameraViewDidFinished(_ viewController: JointViewController)
func OneGroupDidFinished(_ viewController: JointViewController)
func ConfirmDesiredGoal(_ viewController: JointViewController)
}
同时我们应该将CameraView更新为:
// Need UIViewControllerRepresentable to show any UIViewController in SwiftUI
struct CameraView : UIViewControllerRepresentable {
@Binding var title: String
@Binding var isBegin: Bool
@Binding var isFinished: Bool
@Binding var desiredGoal: Int
@Binding var currentNum: Int
// Init your ViewController
func makeUIViewController(context: Context) -> JointViewController {
let controller = JointViewController()
controller.name = title
controller.desiredGoal = desiredGoal
controller.delegate = context.coordinator
return controller
}
// Tbh no idea what to do here
func updateUIViewController(_ uiViewController: JointViewController, context: Context) {
if isBegin {
uiViewController.begin()
} else {
uiViewController.pause()
}
}
func makeCoordinator() -> CameraView.Coordinator {
return Coordinator(isFinished: $isFinished, currentNum: $currentNum, desiredGoal: $desiredGoal)
}
}
接着在便可在SwiftUI view中直接调用CameraView:
struct ContentView: View {
@Binding var title: String
@State var isBegin = false
@State var isFinished = false
@State var desiredGoal = 10
@State var currentNum = 0
var body: some View {
CameraView(title: $title, isBegin: $isBegin, isFinished: $isFinished, desiredGoal: $desiredGoal, currentNum: $currentNum)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
}
}
这里代码没有给完全,不过整体框架便是如此
生命周期
每个表示UIKit视图或视图控制器的SwiftUI视图都经历以下步骤,以总结其生命周期:
- 创建一个自定义
Coordinator实例,该实例管理UIViewController或UIView与SwiftUI应用程序其他部分之间的更新 - 创建
UIViewController或UIView的实例。使用中的信息来context连接协调器,并设置UIViewController或UIView的初始外观。我们可以将此方法简单视为viewDidLoad update每当您更改封闭的SwiftUI视图的状态时,SwiftUI都会自动调用该方法。使用此方法可以使您的信息UIView或UIViewController与更新的状态信息保持同步。- 像您通常会在
deinit中做的一样执行任何清理工作。也就是说移除NotificationCenter observation,无效的计时器或者取消URLSessionTask之类的工作
最后
参考:Using UIView and UIViewController in SwiftUI
推荐:iOS技术资料|地址