Можно ли изменить размер текста с помощью эффекта согласованной геометрии?
У меня есть переход с наложением, в котором используется эффект , как показано в этом видео: согласованной геометрииЭффект согласованной геометрии с текстом того же размера.
Я хочу увеличить переходящий текст после анимации, но кажется, что текстовый фрейм не масштабируется во времени, что приводит к укорачиванию текста при переходе. Эффект согласованной геометрии с текстом разного размера
Есть ли способ преодолеть это?
Исходный текст:
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()
}
}
}
}
}