Created
March 31, 2026 15:12
-
-
Save codewithsanthoshofficial/16c0c4a7a1e283238ee77892d575698a to your computer and use it in GitHub Desktop.
UIKit->SwiftUI && SwiftUI -> UIKit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| π§ Mental Model (Final) | |
| =============================================== | |
| UIKit β SwiftUI | |
| Use UIHostingController | |
| SwiftUI β UIKit | |
| Use UIViewRepresentable / UIViewControllerRepresentable |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| if uiImages.count > 1 { | |
| let swiftUIView = FullImageViewer(images: uiImages) | |
| // Wrap it in a UIHostingController | |
| let hostingController = UIHostingController(rootView: swiftUIView) | |
| // Present it | |
| hostingController.modalPresentationStyle = .fullScreen | |
| self.present(hostingController, animated: true, completion: nil) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 1. Your SwiftUI View (same as before) | |
| --------------------------------------- | |
| struct CircleChart: View { | |
| @State private var progress: Float = 65 | |
| var body: some View { | |
| VStack { | |
| CircularProgressBarWrapper( | |
| totalValue: 100, | |
| subValue: $progress | |
| ) | |
| .frame(width: 200, height: 200) | |
| Button("Increase") { | |
| if progress < 100 { progress += 5 } | |
| } | |
| } | |
| } | |
| } | |
| ========================= | |
| 2. Use in UIKit (UIViewController) | |
| --------------------------------------- | |
| import UIKit | |
| import SwiftUI | |
| class ViewController: UIViewController { | |
| override func viewDidLoad() { | |
| super.viewDidLoad() | |
| let swiftUIView = CircleChart() | |
| // Wrap SwiftUI inside UIKit | |
| let hostingController = UIHostingController(rootView: swiftUIView) | |
| // Add as child VC | |
| addChild(hostingController) | |
| view.addSubview(hostingController.view) | |
| hostingController.view.translatesAutoresizingMaskIntoConstraints = false | |
| NSLayoutConstraint.activate([ | |
| hostingController.view.centerXAnchor.constraint(equalTo: view.centerXAnchor), | |
| hostingController.view.centerYAnchor.constraint(equalTo: view.centerYAnchor), | |
| hostingController.view.widthAnchor.constraint(equalToConstant: 250), | |
| hostingController.view.heightAnchor.constraint(equalToConstant: 300) | |
| ]) | |
| hostingController.didMove(toParent: self) | |
| } | |
| } | |
| ========================= | |
| UIKit (UIViewController) | |
| β | |
| βΌ | |
| UIHostingController | |
| β | |
| βΌ | |
| SwiftUI View (CircleChart) | |
| β | |
| βΌ | |
| @State updates UI automatically | |
| ========================= | |
| Two-way communication (important) | |
| If UIKit needs to talk to SwiftUI, you use: | |
| Option 1: ObservableObject (most common) | |
| --------------------------------------- | |
| class ProgressViewModel: ObservableObject { | |
| @Published var progress: Float = 50 | |
| } | |
| ================ | |
| SwiftUI: | |
| --------------------------------------- | |
| struct CircleChart: View { | |
| @ObservedObject var viewModel: ProgressViewModel | |
| var body: some View { | |
| Text("\(viewModel.progress)") | |
| } | |
| } | |
| ================ | |
| UIKit: | |
| --------------------------------------- | |
| let viewModel = ProgressViewModel() | |
| let swiftUIView = CircleChart(viewModel: viewModel) | |
| viewModel.progress = 80 // UIKit β SwiftUI update | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import SwiftUI | |
| struct CircleChart: View { | |
| @State private var progress: Float = 65 | |
| var body: some View { | |
| CircularProgressBarWrapper(totalValue: 100, subValue: progress) | |
| // Example to change progress dynamically | |
| Button("Increase") { | |
| if progress < 100 { progress += 5 } | |
| } | |
| .padding(.top, 20) | |
| } | |
| } | |
| #Preview { | |
| CircleChart() | |
| } | |
| =============================== | |
| import SwiftUI | |
| import UIKit | |
| struct CircularProgressBarWrapper: UIViewRepresentable { | |
| var totalValue: Float | |
| var subValue: Float | |
| func makeUIView(context: Context) -> CircularProgressBarView { | |
| return CircularProgressBarView() | |
| } | |
| func updateUIView(_ uiView: CircularProgressBarView, context: Context) { | |
| uiView.configure(total: totalValue, progress: subValue) | |
| } | |
| } | |
| ================================================= | |
| import Foundation | |
| import UIKit | |
| class CircularProgressBarView: UIView { | |
| private let shapeLayer = CAShapeLayer() | |
| private let trackLayer = CAShapeLayer() | |
| private let label = UILabel() | |
| override init(frame: CGRect) { | |
| super.init(frame: frame) | |
| setup() | |
| } | |
| required init?(coder: NSCoder) { | |
| super.init(coder: coder) | |
| setup() | |
| } | |
| private func setup() { | |
| layer.addSublayer(trackLayer) | |
| layer.addSublayer(shapeLayer) | |
| label.textAlignment = .center | |
| addSubview(label) | |
| } | |
| override func layoutSubviews() { | |
| super.layoutSubviews() | |
| let center = CGPoint(x: bounds.midX, y: bounds.midY) | |
| let radius = bounds.width / 3 | |
| let start = -CGFloat.pi / 2 | |
| let end = 3 * CGFloat.pi / 2 | |
| let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: start, endAngle: end, clockwise: true) | |
| trackLayer.path = path.cgPath | |
| trackLayer.strokeColor = UIColor.lightGray.cgColor | |
| trackLayer.lineWidth = 10 | |
| trackLayer.fillColor = UIColor.clear.cgColor | |
| shapeLayer.path = path.cgPath | |
| shapeLayer.strokeColor = UIColor.blue.cgColor | |
| shapeLayer.lineWidth = 10 | |
| shapeLayer.fillColor = UIColor.clear.cgColor | |
| shapeLayer.strokeEnd = 0 | |
| label.frame = bounds | |
| } | |
| func configure(total: Float, progress: Float) { | |
| let percentage = CGFloat(progress / total) | |
| shapeLayer.strokeEnd = percentage | |
| label.text = "\(Int(progress))%" | |
| } | |
| } | |
| ===================================== |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import SwiftUI | |
| struct CircleChart2: View { | |
| @State private var progress: Float = 65 | |
| var body: some View { | |
| CircularProgressBarWrapper2( totalValue: 100, subValue: $progress) // π binding | |
| .frame(width: 200, height: 200) | |
| Button("Increase") { | |
| if progress < 100 { progress += 5 } | |
| } | |
| .padding(.top, 20) | |
| } | |
| } | |
| #Preview { | |
| CircleChart2() | |
| } | |
| ========================================== | |
| import Foundation | |
| import SwiftUI | |
| struct CircularProgressBarWrapper2:UIViewRepresentable { | |
| var totalValue: Float | |
| @Binding var subValue: Float //now binding | |
| func makeCoordinator() -> Coordinator { | |
| Coordinator(self) | |
| } | |
| func makeUIView(context: Context) -> CircularProgressBarView2 { | |
| let view = CircularProgressBarView2() | |
| view.delegate = context.coordinator | |
| return view | |
| } | |
| func updateUIView(_ uiView: CircularProgressBarView2, context: Context) { | |
| uiView.configure(total: totalValue, progress: subValue) | |
| } | |
| } | |
| class Coordinator: NSObject, CircularProgressBarDelegate { | |
| var parent: CircularProgressBarWrapper2 | |
| init(_ parent: CircularProgressBarWrapper2) { | |
| self.parent = parent | |
| } | |
| func didTapProgress(newValue: Float) { | |
| parent.subValue = newValue // π UIKit β SwiftUI | |
| } | |
| } | |
| ================================================== | |
| import Foundation | |
| import UIKit | |
| //updated - protocol two data flow | |
| protocol CircularProgressBarDelegate: AnyObject { | |
| func didTapProgress(newValue: Float) | |
| } | |
| class CircularProgressBarView2: UIView { | |
| private let shapeLayer = CAShapeLayer() | |
| private let trackLayer = CAShapeLayer() | |
| private let label = UILabel() | |
| weak var delegate: CircularProgressBarDelegate? | |
| private var currentProgress: Float = 0 | |
| private var totalValue: Float = 100 | |
| override init(frame: CGRect) { | |
| super.init(frame: frame) | |
| setup() | |
| } | |
| required init?(coder: NSCoder) { | |
| super.init(coder: coder) | |
| setup() | |
| } | |
| private func setup() { | |
| layer.addSublayer(trackLayer) | |
| layer.addSublayer(shapeLayer) | |
| label.textAlignment = .center | |
| addSubview(label) | |
| // Add tap gesture | |
| let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap)) | |
| addGestureRecognizer(tap) | |
| } | |
| override func layoutSubviews() { | |
| super.layoutSubviews() | |
| let center = CGPoint(x: bounds.midX, y: bounds.midY) | |
| let radius = bounds.width / 3 | |
| let start = -CGFloat.pi / 2 | |
| let end = 3 * CGFloat.pi / 2 | |
| let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: start, endAngle: end, clockwise: true) | |
| trackLayer.path = path.cgPath | |
| trackLayer.strokeColor = UIColor.lightGray.cgColor | |
| trackLayer.lineWidth = 10 | |
| trackLayer.fillColor = UIColor.clear.cgColor | |
| shapeLayer.path = path.cgPath | |
| shapeLayer.strokeColor = UIColor.blue.cgColor | |
| shapeLayer.lineWidth = 10 | |
| shapeLayer.fillColor = UIColor.clear.cgColor | |
| label.frame = bounds | |
| } | |
| func configure(total: Float, progress: Float) { | |
| totalValue = total | |
| currentProgress = progress | |
| let percentage = CGFloat(progress / total) | |
| shapeLayer.strokeEnd = percentage | |
| label.text = "\(Int(progress))%" | |
| } | |
| @objc private func handleTap() { | |
| // Example: increase progress on tap | |
| let newValue = min(currentProgress + 5, totalValue) | |
| delegate?.didTapProgress(newValue: newValue) | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
UIKit β SwiftUI
Use UIHostingController
SwiftUI β UIKit
Use UIViewRepresentable / UIViewControllerRepresentable