Reputation: 1
I am trying to read the width of my Text depending on size of Text Font, As we know GeometryReader takes all possible given place to him, in this codes it just take himself the given frame size, that I passed it, but it does not take size of my Text! what I am doing Wrong? I what GeometryReader start reading my Text size only! not himself frame width.
Here is my code:
struct ContentView: View {
@State var fontSize: CGFloat = 20.0
var body: some View {
Spacer()
textWidthGeometryReader(fontSize: $fontSize)
Spacer()
Text("Font size:" + "\(fontSize)")
Slider(value: $fontSize, in: 20...40, step: 1)
.padding()
Spacer()
}
}
struct textWidthGeometryReader: View {
@Binding var fontSize: CGFloat
var body: some View {
GeometryReader { inSideGeometry in
Text("width of Text:" + String(format: "%.0f", inSideGeometry.size.width))
.font(.system(size: fontSize))
.background(Color.yellow)
.position(x: inSideGeometry.size.width / 2, y: inSideGeometry.size.height / 2)
}
.frame(width: 400, height: 300, alignment: .center)
.background(Color.gray)
.cornerRadius(20)
}
}
Upvotes: 13
Views: 18493
Reputation: 3147
another approach is not to render at all (this will allow you to do calculations from stateless code like extension):
extension String
{
func sizeUsingFont(usingFont font: UIFont) -> CGSize
{
let fontAttributes = [NSAttributedString.Key.font: font]
return self.size(withAttributes: fontAttributes)
}
}
and use it like this:
// calculate render width and height of text using provided font (without actually rendering)
let sizeOfText: CGSize = "test string".sizeUsingFont(usingFont: UIFont.systemFont(ofSize: 40, weight: UIFont.Weight.bold))
source i used: How to figure out the width of a string dynamically in SwiftUI
and if you still decide to use GeometryReader, remember that you can put the GeometryReader inside the .background() and save it to a state var on-appear or on-whatever event fits you:
import Foundation
import SwiftUI
struct TestView: View
{
@State var sizeOfText: CGSize = CGSize(width: 0, height: 0)
var body: some View
{
Text("Hello again")
.font(Font.system(size:60, design: Font.Design.rounded))
.background(GeometryReader { (geometryProxy : GeometryProxy) in
HStack {}
.onAppear {
sizeOfText = geometryProxy.size
print("sizeOfText: \(sizeOfText)")
}
})
.offset(x: sizeOfText.width, y: sizeOfText.height)
}
}
Upvotes: 8
Reputation: 1
I found another way to solving this issue, this time you are able to work with .onReceive as well, see my code and make it better, this way is Version 3.0.0! LoL
Code:
class TextModel: ObservableObject
{
@Published var sizeOfFont: CGFloat = 20.0
@Published var sizeOfText: CGSize = .zero
}
struct ContentView: View
{
@StateObject var textModel = TextModel()
var body: some View
{
Spacer()
Text("Size of Text: " + String(format: "%.0f", textModel.sizeOfText.width) + "⭐︎" + String(format: "%.0f", textModel.sizeOfText.height))
.font(.system(size: textModel.sizeOfFont))
.background(Color.yellow)
.background( sizeOfViewOptionA(textModel: textModel) )
Spacer()
Text("Size of Text: " + String(format: "%.0f", textModel.sizeOfText.width) + "⭐︎" + String(format: "%.0f", textModel.sizeOfText.height))
.font(.system(size: textModel.sizeOfFont))
.background(Color.red)
.background( sizeOfViewOptionB(textModel: textModel) )
Spacer()
Text("Font size:" + "\(textModel.sizeOfFont)")
Slider(value: $textModel.sizeOfFont, in: 20...40, step: 1)
.padding()
Spacer()
}
}
struct sizeOfViewOptionA: View
{
@ObservedObject var textModel: TextModel
var body: some View {
GeometryReader { proxy in
HStack{}
.onAppear() {textModel.sizeOfText = proxy.size}
.onChange(of: textModel.sizeOfFont) { _ in textModel.sizeOfText = proxy.size}
}
}
}
struct sizeOfViewOptionB: View
{
@ObservedObject var textModel: TextModel
var body: some View {
GeometryReader { proxy in
HStack{ Color.clear }
.onReceive(textModel.$sizeOfFont) { _ in textModel.sizeOfText = proxy.size}
}
}
}
Upvotes: 0
Reputation: 1
After spending time on GeometryReader I find a easer way to get the size of any view also Text and I just wanted Answer my Question, Go ahead try my Code or Refactor it or make it more smaller if you can, I am pleased to see your way, here what I made:
struct ContentView: View {
@State var sizeOfText: CGSize = .zero
@State var fontSizeOfText: CGFloat = 20.0
var body: some View {
Text("Size of Text: " + String(format: "%.0f", sizeOfText.width) + "⭐︎" + String(format: "%.0f", sizeOfText.height))
.font(.system(size: fontSizeOfText))
.background(Color.yellow)
.background(sizeOfView(fontSizeOfText: $fontSizeOfText, sizeOfText: $sizeOfText))
Spacer()
Text("Font size:" + "\(fontSizeOfText)")
Slider(value: $fontSizeOfText, in: 20...40, step: 1)
.padding()
Spacer()
}
}
struct sizeOfView: View {
@Binding var fontSizeOfText: CGFloat
@Binding var sizeOfText: CGSize
var body: some View {
GeometryReader { proxy in
HStack {}
.onAppear { sizeOfText = proxy.size }
.onChange(of: fontSizeOfText) { _ in sizeOfText = proxy.size }
}
}
}
Upvotes: 3
Reputation: 54466
You can use view preferences.
PreferenceKey
for the view size:struct ViewSizeKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
ViewSizeKey
:struct ViewGeometry: View {
var body: some View {
GeometryReader { geometry in
Color.clear
.preference(key: ViewSizeKey.self, value: geometry.size)
}
}
}
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:
Upvotes: 18