Skip to content

Instantly share code, notes, and snippets.

@codewithsanthoshofficial
Created March 31, 2026 15:12
Show Gist options
  • Select an option

  • Save codewithsanthoshofficial/16c0c4a7a1e283238ee77892d575698a to your computer and use it in GitHub Desktop.

Select an option

Save codewithsanthoshofficial/16c0c4a7a1e283238ee77892d575698a to your computer and use it in GitHub Desktop.
UIKit->SwiftUI && SwiftUI -> UIKit
🧠 Mental Model (Final)
===============================================
UIKit β†’ SwiftUI
Use UIHostingController
SwiftUI β†’ UIKit
Use UIViewRepresentable / UIViewControllerRepresentable
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)
}
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
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))%"
}
}
=====================================
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)
}
}
@codewithsanthoshofficial

Copy link
Copy Markdown
Author

UIKit β†’ SwiftUI

Use UIHostingController

SwiftUI β†’ UIKit

Use UIViewRepresentable / UIViewControllerRepresentable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment