SwiftUI matchedGeometryЭффект «Предупреждение о нескольких вставленных видах в сопоставленной паре геометрических групп» при просмотре, встроенном в кнопку

#ios #swift #animation #swiftui

#iOS #swift #Анимация #swiftui

Вопрос:

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

Я использую matchedGeometryEffect следующим образом:

        HStack(spacing: 15) {
            ForEach(categories, id: .self) { category in
                let isSelected = selectedVal == category
                Button {
                    withAnimation {
                        selectedVal = category
                    }
                } label: {
                    VStack(spacing: 4) {
                        Text(category)
                            .frame(width: 100)
                        if isSelected {
                            RoundedRectangle(cornerRadius: 5)
                                .frame(width: 50, height: 2)
                                .matchedGeometryEffect(id: "Category", in: animation, isSource: isSelected)
                        }
                    }
                }
            }
        }
 

Этот подход работает с предупреждением о Multiple inserted views in matched geometry group Pair<String, ID>(first: "Category", second: SwiftUI.Namespace.ID(id: 10)) have isSource: true, results are undefined.

Когда я извлекаю RoundedRectangle из Button , предупреждение исчезает:

       HStack(spacing: 15) {
            ForEach(categories, id: .self) { category in
                let isSelected = selectedVal == category
                VStack(spacing: 4) {
                    Button(category) {
                        withAnimation {
                            selectedVal = category
                        }
                    }
                    
                    if isSelected {
                        RoundedRectangle(cornerRadius: 5)
                            .frame(width: 50, height: 2)
                            .matchedGeometryEffect(id: "Category", in: animation, isSource: isSelected)
                    }
                }
            }
        }
 

Я подозреваю, что это как-то связано с Button тем, что он не был удален из иерархии представлений в первом решении и, следовательно, все еще содержит ссылку на RoundedRectangle , что сбивает с толку matchedGeometry, поскольку вы не можете видеть исходный вид «видимым», но я бы isSource хотел, чтобы установка в выбранное состояние решила эту проблему. Есть ли объяснение, почему я получаю предупреждение в первом сценарии?

Комментарии:

1. Что касается меня, это похоже на ошибку SwiftUI.

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

Ответ №1:

Проблема в том, что вы можете установить matchedgeometryeffect с одним и тем же пространством имен только для одного представления одновременно. чтобы SwiftUI мог определить, какой объект нужно анимировать. и вот вы устанавливаете геометрический эффект для всех трех представлений с одним и тем же пространством имен, поэтому SwiftUI не может понять, как анимировать и компоновать все представления.

 struct ContentView: View {
    
    enum Tab: String {
        case one
        case two
        case three
    }
    
    @State private var selected: Tab = .one
    @Namespace private var tabNameSpace
    
    var body: some View {
        ZStack {
            HStack(spacing: 32) {
                
                ZStack {
                    if selected == .one {
                        Color.red
                            .frame(width: 100, height: 50)
                            .matchedGeometryEffect(id: "namespace", in: tabNameSpace)
                    }
                    
                    Text(Tab.one.rawValue)
                        .frame(width: 100, height: 50)
                        .onTapGesture {
                            withAnimation(.spring()) {
                                selected = .one
                            }
                        }
                }
                
                ZStack {
                    if selected == .two {
                        Color.red
                            .frame(width: 100, height: 50)
                            .matchedGeometryEffect(id: "namespace", in: tabNameSpace)
                    }
                    
                    Text(Tab.two.rawValue)
                        .frame(width: 100, height: 50)
                        .onTapGesture {
                            withAnimation(.spring()) {
                                selected = .two
                            }
                        }
                }
                
                ZStack {
                    if selected == .three {
                        Color.red
                            .frame(width: 100, height: 50)
                            .matchedGeometryEffect(id: "namespace", in: tabNameSpace)
                    }
                    
                    Text(Tab.three.rawValue)
                        .frame(width: 100, height: 50)
                        .onTapGesture {
                            withAnimation(.spring()) {
                                selected = .three
                            }
                        }
                }
                
            }
        }
    }
    
}