Можно ли изменить размер текста с помощью эффекта согласованной геометрии?

У меня есть переход с наложением, в котором используется эффект , как показано в этом видео: согласованной геометрииЭффект согласованной геометрии с текстом того же размера.

Я хочу увеличить переходящий текст после анимации, но кажется, что текстовый фрейм не масштабируется во времени, что приводит к укорачиванию текста при переходе. Эффект согласованной геометрии с текстом разного размера

Есть ли способ преодолеть это?

Исходный текст:

      Text(info.name)
    .font(.system(size: 22, weight: .bold))
    .matchedGeometryEffect(id: info.name, in: namespace)

Перенесенный текст:

      Text(currentCard.name)
    .font(.title.bold())
    .matchedGeometryEffect(id: currentCard.name, in: namespace)

Спасибо

2 ответа

Наконец-то разобрался!

Добавить отдельный размер шрифта@State varчто вы вызываете внутри вашегоmatchedGeometryEffectхifзаявление.onAppear. Соедините это сAnimatableCustomFontModifierи размер шрифта будет анимироваться просто отлично!

Я знаю. Звучит грубо. Вы не поверите, сколько времени это заняло. Вот рабочий пример.

      struct ContentView: View {
    
    @Namespace var namespace
    @State var hero = false
    @State var heroFontLarge = false
    

    var body: some View {
        VStack {
            if hero {
                Text("hello")
                    .animatableFont(name: "San Francisco", size: heroFontLarge ? 64 : 16)
                    .matchedGeometryEffect(id: "title", in: namespace)
                    // Start the font size animation. Only gets called once the transition starts
                    .onAppear {
                        withAnimation(.linear(duration: 1)) {
                            heroFontLarge = true // Start animating a larger font
                        }
                    }
                    .transition(.scale(scale: 1)) // Stops a fading out bug
            } else {
                Text("hello")
                    .animatableFont(name: "San Francisco", size: heroFontLarge ? 64 : 16)
                    .matchedGeometryEffect(id: "title", in: namespace)
                    // Start the font size animation. Only gets called once the transition starts
                    .onAppear {
                        withAnimation(.linear(duration: 1)) {
                            heroFontLarge = false  // Start animating a smaller font
                        }
                    }
                    .transition(.scale(scale: 1)) // Stops a fading out bug
                
            }
            Button("Toggle") {
                withAnimation(.linear(duration: 1)) {
                    hero.toggle()
                }
            }
        }
    }
}



// A modifier that animates a font through various sizes.
// https://www.hackingwithswift.com/quick-start/swiftui/how-to-animate-the-size-of-text
struct AnimatableCustomFontModifier: ViewModifier, Animatable {
    var name: String
    var size: Double

    var animatableData: Double {
        get { size }
        set { size = newValue }
    }

    func body(content: Content) -> some View {
        content
            .font(.custom(name, size: size))
    }
}

// To make that easier to use, I recommend wrapping
// it in a `View` extension, like this:
extension View {
    func animatableFont(name: String, size: Double) -> some View {
        self.modifier(AnimatableCustomFontModifier(name: name, size: size))
    }
}

К сожалению, это немного глючит, если вы пытаетесь обратить анимацию до ее завершения. Не должно быть проблем, если вы можете отложить взаимодействие до завершения анимации.

Я понимаю, что текст не анимируется с помощью matchedGeometryEffect (macOS 12/Xcode 13). Я не мог найти способ сделать это. Либо работает matchedGeo, либо AnimatableModifier на шрифте, но не оба одновременно.

В некоторых случаях вы можете обойти это, заставив размеры контейнера исходного текста и размера контейнера текста с переходом оставаться одинаковыми с модификатором .frame. Это приводит к тому, что во время анимации по крайней мере не отображаются эллипсы ("..."):

      struct ContentView: View {
    @State private var showHero = false
    @Namespace var ns_hero
    
    var body: some View {
        VStack {
            if showHero == false {
                VStack(alignment: .center) {
           
                    Spacer()
                    
                    VStack {
                        Image("Red")
                            .resizable()
                            .scaledToFit()
                            .matchedGeometryEffect(id: "image", in: ns_hero)
                            .frame(maxWidth: 200, maxHeight: 200)
                        
                        Text("Red")
                            .font(.body)
                            .frame(maxWidth: 300, maxHeight: 50)
                            .matchedGeometryEffect(id: "title", in: ns_hero)
                    }
                    .onTapGesture {
                        withAnimation {
                            showHero.toggle()
                        }
                    }
                }
            } else {
                VStack(alignment: .center) {
                    
                    VStack {
                        Image("Red")
                            .resizable()
                            .scaledToFit()
                            .matchedGeometryEffect(id: "image", in: ns_hero)
                            .frame(maxWidth: 400, maxHeight: 400)
                        
                        Text("Red Rectangle")
                            .font(.largeTitle)
                            .frame(maxWidth: 300, maxHeight: 50)
                            .matchedGeometryEffect(id: "title", in: ns_hero)
                    }
                    .onTapGesture {
                        withAnimation {
                            showHero.toggle()
                        }
                    }
                    
                    Spacer()
                }
            }
        }
    }
}

Другие вопросы по тегам