1

I am trying to adjust a views height depending on its x position on the screen.

Using GeometryReader to get the views current position i then determine if it is on the screen or not and if so, depending on its position from left to right, increase the height accordingly.

My issue is that the state variable doesn't seem to be changing despite me setting it. Im sure its hitting the point where viewHeight is set by using breakpoints and the fact it is printing out the same value

struct MatchView: View {
    public let match: Match
    private let screenWidth = UIScreen.main.bounds.width

    private var cutShape: some Shape {
        CutCorners(cutSize: 8, corners: [.topLeading, .topTrailing, .bottomLeading, .bottomTrailing])
    }

    let isLastMatch: Bool
    @State private var viewHeight: CGFloat = .infinity {
        didSet {
            print(viewHeight) ----> Always prints .inf
        }
    }

    private var frameWidthSize: CGFloat {
        device.isPhone ? 250 : 300
    }

    var body: some View {
        VStack(alignment: .leading, spacing: zeroSpacing) {
            Text(match.teams[0].name)
        }
        .clipShape(cutShape)
        .overlay(cutShape.stroke(Color.Riot.Grey500, lineWidth: 1))
        .frame(width: frameWidthSize)
        .frame(maxHeight: viewHeight)
        .background(
            GeometryReader { gp -> Color in
                if isLastMatch {
                    self.setViewHeight(viewRect: gp.frame(in: .global))
                }
                return Color.yellow
            }
        )
        .animation(.default, value: viewHeight)
    }

    private func setViewHeight(viewRect: CGRect) {
        let viewX = viewRect.minX
        let height = viewRect.height
        if viewX > 0, viewX < screenWidth {
            let viewXAsPercent = viewX / screenWidth
            let newHeight = viewXAsPercent * height
            if newHeight > 130 {
                withAnimation {
                    viewHeight = newHeight
                }
            }
        } else {
            withAnimation {
                viewHeight = height - 130
            }
        }
    }
}

Can anyone see why this is happening?

I have also tried extracting setViewHeight into a ViewModel with a viewHeight published var and pointing like this.frame(maxHeight: viewModel.viewHeight) but i cant get it to work.

Wazza
  • 1,725
  • 2
  • 17
  • 49

1 Answers1

0

Couple of issues: didSet isn't called on @State vars you have to watch them using .onChange. You can't set state from within body, it has to be done in an action.

The correct solution to passing the size form the geometry reader back up the hierarchy is using SwiftUI's preference system, i.e. PreferenceKey, .preference and .onPreferenceChange. There are plenty of examples of this but here is one I found quickly:

https://stackoverflow.com/a/72751444/259521

malhal
  • 26,330
  • 7
  • 115
  • 133
  • btw, this is not working also, tried to make scrollProgressReader, but continue getting warning on console [SwiftUI] Publishing changes from within view updates is not allowed, this will cause undefined behavior. https://stackoverflow.com/questions/74931720/swiftui-get-scrolling-progress-with-geometry-reader – George Heints Dec 27 '22 at 17:35