Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI Text clips on resize

In the following sample, tapping the 'Expand' button causes the text '39' to clip as it resizes in transition to '40'. The real context of this use is a text label that reflects the value of a picker in an animated diagram. I'd like for the text to only take up the space that it requires, but without clipping as it animates between values.

Disabling animations for the label isn't an option, as the label's position in my diagram also animates.

Reproduction:

import SwiftUI

struct TextClipping: View {
    enum ExpansionState {
        case expanded
        case contracted

        mutating func toggle() {
            switch self {
            case .expanded:
                self = .contracted
            case .contracted:
                self = .expanded
            }
        }
    }

    @State var expansion: ExpansionState = .contracted

    var text: String {
        switch expansion {
        case .expanded:
            return "40"
        case .contracted:
            return "39"
        }
    }

    var body: some View {
        VStack(spacing: 16) {
            Text(text)
                .font(.system(.title, design: .rounded))
                .fontWeight(.bold)
                .foregroundColor(.black)

            Button(self.expansion == .contracted ? "Expand" : "Contract") {
                withAnimation {
                    self.expansion.toggle()
                }
            }
        }
    }
}

struct TextClipping_Previews: PreviewProvider {
    static var previews: some View {
        TextClipping()
    }
}

Any ideas for how I might allow the text of the label to change fluidly, without clipping?

like image 805
Michael Pangburn Avatar asked Oct 20 '19 19:10

Michael Pangburn


1 Answers

So, I don't think it was entirely clear by your question, but I assume the "clipping" you were referring to was the 39 and 40 moving slightly left and right as you animate between them. In a parent view, this might clip, but I don't see it here. First things first, you don't need to make your own mutating struct to modify a variable in a view, this is the whole point of @State variables, and this whole issue can be solved with a simple bool. All of this code is essentially the same, except for one key line: the .id line. This tells SwiftUI that the views are not the same. Before adding it, it thought they were the same view, but the text was changing, causing the offsets to look weird. If this still doesn't answer your question let me know but I cant imagine what else you'd be referring to, as you did not include a visual example and I'm unable to reproduce the "clipping" effect you're referring to.

struct TextClipping: View {
    @State private var expanded: Bool = false
    var body: some View {
        let text: String = expanded ? "40" : "39"
        VStack(spacing: 16) {
             Text(text)
                .font(.system(.title, design: .rounded))
                .fontWeight(.bold)
                .foregroundColor(.black)
                // Opacity is the default animation, but can be replaced by another if you feel like it
                //.transition(.opacity)
                .id("label\(text)")

            Button(expanded ? "Contract" : "Expand") {
                withAnimation {
                    expanded.toggle()
                }
            }
        }
    }
}

struct TextClipping_Previews: PreviewProvider {
    static var previews: some View {
        TextClipping()
    }
}
like image 53
Vera Gonzalez Avatar answered Jan 25 '23 08:01

Vera Gonzalez