UIViewRepresentable protocl을 활용해야 합니다
UIViewRepresentable를 따르는 struct를 만들어서 SwiftUI에서 바로 사용하면 됩니다.
이 struct는 반드시 두 가지 메소드를 구현해야 합니다.
final class RedLabel: UILabel {
    override func awakeFromNib() {
        super.awakeFromNib()
        textColor = .red
    }
}
struct RepresentableRedLabel: UIViewRepresentable  {
    var text: String
    let redLabel = RedLabel()
    func makeUIView(context: Context) -> UILabel {
        redLabel
    }
    func updateUIView(_ uiView: UILabel, context: Context) {
        redLabel.text = text
    }
}
struct ContentView: View {
    var body: some View {
        RepresentableRedLabel(text: "내용")
    }
}
// UILabel의 RedLabel을 SwiftUI에서 사용하는 경우입니다.
UIViewController를 SwiftUI에서 사용하기 위해서는 UIViewControllerRepresentable가 필수입니다.
UIViewControllerRepresentable를 구현하기 위해서는 반드시 두 가지 메소드를 구현해야합니다.
– func makeUIViewController(context: Context) -> some UIViewController
– func updateUIViewController(_ uiViewController: some UIViewController, context: Context)

struct ImagePickerController: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> UIImagePickerController{
        UIImagePickerController()
    }
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
}
struct ContentView: View {
    var body: some View {
        ImagePickerController()
    }
}
UIImgePickerController에서 선택한 UIImage를 Coordinator를 사용해서 SwiftUI에 표시하는 코드

struct ImagePickerController: UIViewControllerRepresentable {
    // SwiftUI 에서의 부모뷰의 @State property부터의 Binding
    @Binding var selectedImage: Image?
    @Binding var existSelectedImage: Bool
 
    func makeUIViewController(context: Context) -> UIImagePickerController {
        let imagePickerController = UIImagePickerController()
        imagePickerController.delegate = context.coordinator
 
        return imagePickerController
    }
 
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
 
    //  Cordinator 대입.
    func makeCoordinator() -> Coordinator {
        Coordinator(selectedImage: $selectedImage, existSelectedImage: $existSelectedImage)
    }
 
}
 
extension ImagePickerController {
    // Cordinator 만들기
    final class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        @Binding var selectedImage: Image?
        @Binding var existSelectedImage: Bool
        // 밑줄을 사용해서 초기화 해야한다. 프로퍼티 래퍼임을 알려주기 위해서.
        init(selectedImage: Binding<Image?>, existSelectedImage: Binding<Bool>) {
            _selectedImage = selectedImage
            _existSelectedImage = existSelectedImage
        }
 
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            guard let selectedOriginalImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { return }
 
            selectedImage = Image(uiImage: selectedOriginalImage)
            existSelectedImage = true
        }
    }
 
}
 
struct ContentView: View {
    @State private var selectedImage: Image?
    @State private var existSelectedImage = false
 
    var body: some View {
        ZStack {
            VStack {
                selectedImage?
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: 100.0, height: 100.0)
                Button(action: didTapSelectedImageButton) {
                    Text("Select Image")
                }
            }
 
            if selectedImage == nil {
                ImagePickerController(
                    selectedImage: $selectedImage,
                    existSelectedImage: $existSelectedImage
                )
            }
        }
    }
 
    private func didTapSelectedImageButton() {
        selectedImage = nil
        existSelectedImage = false
    }
}
// @State property와 Binding으로 Coordinator에서 부모 뷰에 업데이트를 별도로 하지 않아도 간단하게 구현함
기존에 UIView 또는 UIViewController에서 SwiftUI View를 사용할 때에는 UIViewRepresentable을 따르는 Struct를 매번 따로 만들어 줘야할필요 없이 @Bindng할 property가 필요없는 경우에는 UIKit class에서 extension으로 구현이 가능하다.
final class Label: UILabel {}
 
//  Extension 으로 정의
extension Label: UIViewRepresentable {
    func makeUIView(context: Context) -> UILabel {
        backgroundColor = .gray
        text = "This is UIKit UILabel"
 
        return self
    }
 
    func updateUIView(_ uiView: UILabel, context: Context) {}
}
 
struct ContentView: View {
    var body: some View {
        VStack {
            Text("This is Swift UI Text")
            Label()
        }
    }
}
UIHostingController를 사용해야 합니다.
별도로 추가해야하는 method나 property는 없습니다.
UIHostingController(rootView: UIKit에서 표시할 SwiftUI): SwiftUI View를 초기화해서 넣어주기만 하면 끝
struct ContentView: View {
    var body: some View {
        Text("내용")
            .fontWeight(.bold)
    }
}
 
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
 
        
        let hostingController = UIHostingController(rootView: ContentView())
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        hostingController.view.frame = view.bounds
 
        addChild(hostingController)
        view.addSubview(hostingController.view)
    }
}
UIHostingController의 rootView로 SwiftUI를 넣어주기만 하면 된다. addChild, addSubView는 지금까지 UIKit에서 UIViewController를 추가시킬 때와 같다.
struct ContentView: View {
    var body: some View {
        Text("펭 - 하!")
            .fontWeight(.bold)
    }
}
 
class ViewController: UIViewController {
 
    @IBAction private func didTapPresentButton(_ sender: Any) {
        let hostingController = UIHostingController(rootView: ContentView())
        present(hostingController, animated: true)
    }
 
}
SwiftUI 화면을 present 시킬 때도 SwiftUI 뷰를 UIHostingController로 감싸는 것 이외의 UIKit 코드는 지금까지 동일하게 사용하면 된다.
struct ContainerView<Content: View>: View {
    @ViewBuilder var content: Content
    var body: some View {
        content
    }
}
// 뷰에 단순히 넣는것 뿐만 아니라, if-else, switch-case 블럭에서도 사용 가능하다.
struct SimpleView: View {
    var body: some View {
        ContainerView{
            Text("SimpleView Text")
        }
    }
}
struct IfElseView: View{
    var flag = true
    
    var body: some View {
        ContainerView{
            if flag {
                Text("True text")
            } else {
                Text("False text")
            }
        }
    }
}
struct SwitchCaseView: View {
    var condition = 1
    var body: some View {
        ContainerView {
            switch condition {
                case 1:
                    Text("one")
                default:
                    Text("Default")
            }
        }
    }
}
struct ParentView: View {
    var body: some View {
        NavigationView{
            VStack(spacing: 8){
                ChildView(destinationView: Text("View1"), title: "1st")
                ChildView(destinationView: Text("View2"), title: "2nd")
                Spacer()
            }
            .padding(.all)
            .navigationBarTitle("NavigationLinks")
        }
    }
}
struct ChildView<Content: View>: View {
    var destinationView: Content
    var title: String
    init(destinationView: Content,  title: String) {
        self.destinationView = destinationView
        self.title = title
    }
    var body: some View {
        NavigationLink(destination: destinationView){
            Text("This item opens the \(title) view").foregroundColor(Color.black)
        }
    }
}
SwiftUI에서 UITableView는 List입니다.
UIKit에서 사용했던 UITableViewDelegate, UITableViewDataSource 없이 간단히 구현이 가능하다.
struct ContentView: View {
    var body: some View {
        List {
            Text("1")
            Text("2")
            Text("3")
        }
    }
}
따로 커스텀해서 항목을 추가하는것도 간단히 가능하다
struct CustomCell: View {
    var body: some View {
        HStack {
            Image(systemName: "tortoise.fill")
            Text("거북이")
        }
    }
}
 
struct ContentView: View {
    var body: some View {
        List {
            CustomCell() 
            Text("1")
            Text("2")
            Text("3")
        }
    }
}
List가 네비게이션뷰의 하위 뷰로 선언되어있고 Cell이 네비게이션 링크로 감싸져있다면, Cell을 터치하면 다른 뷰로 이동이 가능하다.
List에 표시될 Array의 내용물은 반드시 Identifiable 프로토콜을 따르는 class, struct로 정의 해야한다
struct Item: Identifiable {
    var id = UUID() // Int, String 등 hashable을 따르는 것들은 뭐든지 가능
    let name: String
} 
List 내에서 식별될 수 있는 id 프로퍼티도 반드시 가지고 있어야 한다 !
struct ContentView: View {
    let items: [Item] = [Item(name: "asdf"), Item(name: "sadfasf")]
    var body: some View {
        NavigationView {
            List(items) {item in
                Text(item.name)
            }
            .listStyle(GroupedListStyle())
            .navigationBarTitle("List View")
        }
    }
}
우선 items가 상태 프로퍼티로 존재해야한다. 따라서 위의 코드의 items를 @State private var items 로 바꿔주고 아래 코드처럼 함수를 추가해주면 된다.
List {
    // ForEach로 각각의 cell에 delete 메소드를 입력시켜주어야한다.
    ForEach(items) { item in
            Text(item.name)
        }.onDelete(perform: didDeleteCell)
    }
    .listStyle(GroupedListStyle())
    .navigationBarTitle("List View")
// parameter 에는 반드시 offsets 을 추가해야 함.
func didDeleteCell(at offsets: IndexSet) {
    items.remove(atOffsets: offsets) 
    print("Deleted cell is \(offsets)")
}
수정 코드(셀의 순서 변경 가능)
List {
    ForEach(items) { item in
        Text(item.name)
    }.onMove(perform: didMoveCell)
}
.listStyle(GroupedListStyle())
.navigationBarTitle("List View")
.navigationBarItems(trailing: EditButton()) // 수정 버튼이 들어갈 Item
func didMoveCell(form source: IndexSet, to destination: Int) {}
참조
https://unnnyong.com/2020/05/22/swiftui-list-%ea%b5%ac-uitableview/