Skip to content

Instantly share code, notes, and snippets.

@shaps80
Created July 28, 2023 22:10
Show Gist options
  • Save shaps80/44f82efd1ba0dad2ee8235fdff80f1d8 to your computer and use it in GitHub Desktop.
Save shaps80/44f82efd1ba0dad2ee8235fdff80f1d8 to your computer and use it in GitHub Desktop.
Observe Geometry updates on any SwiftUI view
import SwifUI
public struct Geometry: Equatable {
fileprivate var proxy: GeometryProxy?
public var size: CGSize = .init(width: 10, height: 10)
public var safeAreaInsets: EdgeInsets = .init()
public func frame(in coordinateSpace: CoordinateSpaceProtocol) -> CGRect {
proxy?.frame(in: coordinateSpace) ?? .init(origin: .zero, size: size)
}
public func bounds(of coordinateSpace: NamedCoordinateSpace) -> CGRect {
proxy?.bounds(of: coordinateSpace) ?? .init(origin: .zero, size: size)
}
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.size == rhs.size
&& lhs.safeAreaInsets == rhs.safeAreaInsets
}
}
public extension View {
func onGeometryChange(perform handler: @escaping (Geometry) -> Void) -> some View {
overlay {
GeometryReader { geo in
Color.clear
.preference(key: GeometryPreferenceKey.self, value: Geometry(
proxy: geo,
size: geo.size,
safeAreaInsets: geo.safeAreaInsets
))
}
}
.onPreferenceChange(GeometryPreferenceKey.self) { geo in
handler(geo)
}
}
}
private struct GeometryPreferenceKey: PreferenceKey {
static var defaultValue: Geometry = .init()
static func reduce(value: inout Geometry, nextValue: () -> Geometry) {
value = nextValue()
}
}
@shaps80
Copy link
Author

shaps80 commented Jul 28, 2023

Demo:

import SwiftUI

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    @State private var showText: Bool = true
    
    var body: some View {
        VStack(spacing: 20) {
            Toggle("Toggle", isOn: $showText.animation())
            
            VStack {
                Image(systemName: "globe")
                    .imageScale(.large)
                    .foregroundColor(.accentColor)
                if showText {
                    Text("Hello, world!")
                }
            }
        }
        .padding()
        .onGeometryChange { geo in
            print(geo.size)           // <—— Toggle the text to see size updates trigger this
        }
    }
}

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