How we can get and read size of a Text with GeometryReader in SwiftUI?
You can use view preferences.
- First create a custom
PreferenceKey
for the view size:
struct ViewSizeKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
- Create a view which will calculate its size and assign it to the
ViewSizeKey
:
struct ViewGeometry: View {
var body: some View {
GeometryReader { geometry in
Color.clear
.preference(key: ViewSizeKey.self, value: geometry.size)
}
}
}
- Use them in your view:
struct ContentView: View {
@State var fontSize: CGFloat = 20.0
@State var textSize: CGSize = .zero
var body: some View {
Spacer()
Text("width of Text:" + String(format: "%.0f", textSize.width))
.font(.system(size: fontSize))
.background(ViewGeometry())
.onPreferenceChange(ViewSizeKey.self) {
textSize = $0
}
Spacer()
Text("Font size:" + "\(fontSize)")
Slider(value: $fontSize, in: 20...40, step: 1)
.padding()
Spacer()
}
}
View Preferences is quite an advanced topic. You can find a more detailed explanation here:
- The magic of view preferences in SwiftUI
- Inspecting the View Tree – Part 1: PreferenceKey
How to Align GeometryReader in SwiftUI?
It looks like you placed .frame
modifier in wrong place (placement of modifier is important)
Here is modified code (assuming I understood your intention)
var body: some View {
GeometryReader { geo in
Text("\(randomRoll)")
.font(.title)
.padding(.horizontal)
.frame(maxWidth: .infinity, maxHeight: .infinity) // << before background !!
.background(
Rectangle()
.foregroundColor(Color(hue: min(1, geo.frame(in: .global).minY/CGFloat(randomNumber) ), saturation: 1, brightness: 1))
.cornerRadius(10))
}
.frame(height: 150)
}
How can I read width of view inside a Array of View without using GeometryReader?
Unfortunately, it does not seem possible to get the width and height of the Rectangle without geometry reader in pure SwiftUI. I tried the methods that were attached to that Shape, but nothing resulted in the size.
Unknown what the use case is, but it is possible to make a custom object that holds the width and height of the rectangle.
struct Rectangle_TEST {
var rectangle: Rectangle
var width: CGFloat
var height: CGFloat
init(width: CGFloat, height: CGFloat) {
self.width = width
self.height = height
self.rectangle = Rectangle()
}
func getView(color: Color) -> some View {
return self.rectangle
.fill(color)
.frame(width: self.width, height: self.height)
}
}
struct ContentView: View {
var body: some View {
let arrayOfRec: [Rectangle_TEST] = [Rectangle_TEST(width: 100, height: 100), Rectangle_TEST(width: 200, height: 200)]
VStack {
ForEach(0..<arrayOfRec.count) { index in
Text("Width: \(arrayOfRec[index].width), Height: \(arrayOfRec[index].height)")
arrayOfRec[index].getView(color: .red)
}
}
}
}
Above I made the custom object and stores the width and height that can be accessed individually, and then a function that will return a view, and you can pass in whatever modifiers are needed. But, currently, I don't think it is possible to get the dimensions without geometry reader in SwiftUI.
Get width of a view using in SwiftUI
The only way to get the dimensions of a View
is by using a GeometryReader
. The reader returns the dimensions of the container.
What is a geometry reader? the documentation says:
A container view that defines its content as a function of its own size and coordinate space. Apple Doc
So you could get the dimensions by doing this:
struct ContentView: View {
@State var frame: CGSize = .zero
var body: some View {
HStack {
GeometryReader { (geometry) in
self.makeView(geometry)
}
}
}
func makeView(_ geometry: GeometryProxy) -> some View {
print(geometry.size.width, geometry.size.height)
DispatchQueue.main.async { self.frame = geometry.size }
return Text("Test")
.frame(width: geometry.size.width)
}
}
The printed size is the dimension of the HStack
that is the container of inner view.
You could potentially using another GeometryReader
to get the inner dimension.
But remember, SwiftUI is a declarative framework. So you should avoid calculating dimensions for the view:
read this to more example:
- Make a VStack fill the width of the screen in SwiftUI
- How to make view the size of another view in SwiftUI
SwiftUI - How to get GeometryReader size/height from different View?
You can:
- Make a
@State
property to store the height - Set it using an
.onAppear {
attached toColor.clear
- Replace
???
with\(textHeight)
struct ContentView: View {
@State var textHeight = CGFloat(0) /// 1.
var body: some View {
VStack {
Text("Hello world!")
.background(
GeometryReader { proxy in
Color.clear
.onAppear { /// 2.
textHeight = proxy.size.height
}
}
)
/// 3.
Text("Height of first text is \(textHeight)")
}
}
}
Result:
SwiftUI - Get size of child?
Basically, the answer at this point is to use a GeometryReader
inside of the child's background(...)
modifier.
// This won't be valid until the first layout pass is complete
@State var childSize: CGSize = .zero
var body: some View {
ZStack {
Text("Hello World!")
.background(
GeometryReader { proxy in
Color.clear
.preference(
key: SizePreferenceKey.self,
value: proxy.size
)
}
)
}
.onPreferenceChange(SizePreferenceKey.self) { preferences in
self.childSize = preferences
}
}
struct SizePreferenceKey: PreferenceKey {
typealias Value = CGSize
static var defaultValue: Value = .zero
static func reduce(value: inout Value, nextValue: () -> Value) {
value = nextValue()
}
}
How to get the actual view rendered size and position in SwiftUI?
Actual Rendered Size
The workaround is to get the actual rendered size via .background
modifier with an nested GeometryReader
. The size information inside the new geometry proxy can then be stored in a temporary @State
variable defined in the View.
struct FooSizePreferenceKey: PreferenceKey {
static let defaultValue = CGSize.zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
struct FooView: View {
@State private var fooSize: CGSize = .zero
var body: some View {
GeometryReader { geometryProxy in
VStack {
Text("\(self.fooSize.height), \(self.fooSize.width)")
Text("Foo")
.background(
GeometryReader { fooProxy in
Color
.green
.preference(key: FooSizePreferenceKey.self,
value: fooProxy.size)
.onPreferenceChange(FooSizePreferenceKey.self) { size in
self.fooSize = size
}
}
)
}
}
.frame(width: 300, height: 300)
.background(Color.blue)
}
}
Actual Rendered Position
The actual rendered position for the view can be calculated using Anchor
and anchorPreference
. Using the anchor and the parent geometryProxy
, we can easily get the position .bound
information of the target view.
struct FooAnchorData: Equatable {
var anchor: Anchor<CGRect>? = nil
static func == (lhs: FooAnchorData, rhs: FooAnchorData) -> Bool {
return false
}
}
struct FooAnchorPreferenceKey: PreferenceKey {
static let defaultValue = FooAnchorData()
static func reduce(value: inout FooAnchorData, nextValue: () -> FooAnchorData) {
value.anchor = nextValue().anchor ?? value.anchor
}
}
struct FooView: View {
@State private var foo: CGPoint = .zero
var body: some View {
GeometryReader { geometryProxy in
VStack {
Text("\(self.foo.x), \(self.foo.y)")
Text("Foo")
.background(
GeometryReader { fooProxy in
Color
.green
.anchorPreference(key: FooAnchorPreferenceKey.self,
value: .bounds,
transform: {FooAnchorData(anchor: $0) })
.onPreferenceChange(FooAnchorPreferenceKey.self) { data in
self.foo = geometryProxy[data.anchor!].origin
}
}
)
}
}
.frame(width: 300, height: 300)
.background(Color.blue)
}
}
Related Topics
Embedding Videos in a Tableview Cell
Convert Firebase Firestore Timestamp to Date (Swift)
How to Convert Range in Nsrange
Swift: Extending Functionality of Print() Function
What Are "Intervals" in Swift Ranges
What Does the Swiftui '@State' Keyword Do
Convert Uiimage to Grayscale Keeping Image Quality
Swiftui Create Image Slider with Dots as Indicators
Replacing Calayer and Cabasicanimation with Skscene and Skactions
How to Convert Uicolor to Swiftui's Color
How to Do "Deep Copy" in Swift
Swift: How to Read Standard Output in a Child Process Without Waiting for Process to Finish
Add @Published Behaviour for Computed Property
How to Add Material to Modelentity Programatically in Realitykit
Self' Used Before All Stored Properties Are Initialized