SwiftUI содержит геометрические эффекты и анимацию tabBar
Привет, ребята, у меня есть некоторые проблемы с моим кодом. Я только что немного поэкспериментировал с matchedGeometryEffect в SwiftUI, и он отлично работает. Но теперь я столкнулся с некоторыми проблемами:
Я не могу просто деактивировать tabBar, когда DetailView закрывается, потому что представление немного подпрыгивает.
Переход View иногда глючит, и консоль выдает мне (постоянно) вывод
Multiple inserted views in matched geometry group Pair<String, ID>(first: "bg", second: SwiftUI.Namespace.ID(id: 415)) have `isSource: true`, results are undefined.
Есть ли лучший способ плавно анимировать это и отключить tabBar?
Вот мой код:
struct FullscreenView: View {
@Namespace var animationNamespace
@State var shouldShowFullsceen = false
@State var shouldShowDetails = false
var body: some View {
Input()
.padding()
.onTapGesture {
withAnimation(.interactiveSpring(
response: 0.6,
dampingFraction: 0.7,
blendDuration: 0.7
)) {
shouldShowFullsceen = true
}
}
.overlay {
if shouldShowFullsceen {
Output()
.onTapGesture {
withAnimation(.interactiveSpring(
response: 0.6,
dampingFraction: 0.7,
blendDuration: 0.7
)) {
shouldShowFullsceen = false
shouldShowDetails = false
}
}
}
}
}
}
extension FullscreenView {
@ViewBuilder
func Input() -> some View {
Content()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(BackgroundView())
}
@ViewBuilder
func Output() -> some View {
DetailedContent()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(FullscreenBackground())
}
}
extension FullscreenView {
@ViewBuilder
func Content() -> some View {
Image("dog")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxHeight: 300)
.matchedGeometryEffect(id: "content", in: animationNamespace)
}
}
extension FullscreenView {
@ViewBuilder
func DetailedContent() -> some View {
VStack {
Content()
ScrollView(.vertical) {
Text(dummyText)
.padding()
.opacity(shouldShowDetails ? 1 : 0)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding()
}
.transition(.identity)
.onAppear {
withAnimation(.interactiveSpring(
response: 0.6,
dampingFraction: 0.7,
blendDuration: 0.7
).delay(0.1)) {
shouldShowDetails = true
}
}
}
}
extension FullscreenView {
@ViewBuilder
func BackgroundView() -> some View {
Color.orange
.clipShape(RoundedRectangle(cornerRadius: 15))
.matchedGeometryEffect(id: "bg", in: animationNamespace)
}
}
extension FullscreenView {
@ViewBuilder
func FullscreenBackground() -> some View {
BackgroundView()
.ignoresSafeArea()
}
}
struct FullscreenView_Previews: PreviewProvider {
static var previews: some View {
FullscreenView()
}
}
1 ответ
Относительно анимации и предупреждения консоли:
Не накладывайте вид вывода. Показать входной или выходной вид с помощью
if ... else
, то можно сделать переход.Вы должны использовать
.matchedGeometryEffect
сisSource:
указано значение true как для изображения, так и для фона.избавляться от
.transition(.identity)
.
Вот полный код с комментариями:
struct FullscreenView: View {
@Namespace var animationNamespace
@State var shouldShowFullsceen = false
@State var shouldShowDetails = false
var body: some View {
if shouldShowFullsceen == false { // show only one matched view at a time
Input()
.padding()
.onTapGesture {
withAnimation(.interactiveSpring(
response: 0.6,
dampingFraction: 0.7,
blendDuration: 0.7
)) {
shouldShowFullsceen = true
}
}
} else { // show only one matched view at a time
Output()
.onTapGesture {
withAnimation(.interactiveSpring(
response: 0.6,
dampingFraction: 0.7,
blendDuration: 0.7
)) {
shouldShowFullsceen = false
shouldShowDetails = false
}
}
}
}
func Input() -> some View {
Content()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(BackgroundView())
}
func Output() -> some View {
DetailedContent()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(FullscreenBackground())
}
func Content() -> some View {
Image(systemName: "tortoise")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxHeight: 300)
.padding()
.matchedGeometryEffect(id: "content", in: animationNamespace, isSource: true) // add isSource
}
func DetailedContent() -> some View {
VStack {
Content()
ScrollView(.vertical) {
Text("dummyText")
.padding()
.opacity(shouldShowDetails ? 1 : 0)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding()
}
// .transition(.identity) // take this out
.onAppear {
withAnimation(.interactiveSpring(
response: 0.6,
dampingFraction: 0.7,
blendDuration: 0.7
).delay(0.1)) {
shouldShowDetails = true
}
}
}
func BackgroundView() -> some View {
Color.orange
.clipShape(RoundedRectangle(cornerRadius: 15))
.matchedGeometryEffect(id: "bg", in: animationNamespace, isSource: true) // add isSource
}
func FullscreenBackground() -> some View {
BackgroundView()
.ignoresSafeArea()
}
}