MST
星途 面试题库

面试题:Swift中SwiftUI的TabView与SplitView的深度集成及动画交互设计

设计一个复杂的SwiftUI应用界面,要求深度集成TabView和SplitView。TabView中有四个Tab,分别为“首页”“发现”“消息”“我的”。在“发现”Tab中,使用SplitView实现左右分栏,左侧为分类列表,右侧为对应分类的内容展示。当用户在左侧点击不同分类时,右侧内容展示需要有淡入淡出的动画效果,同时TabView的切换也需要添加自定义的过渡动画。并且在多窗口环境下(如iPad的分屏、Slide Over等),该界面要保持稳定且交互流畅。请详细说明设计思路、涉及到的技术点,并给出完整的代码实现。
34.0万 热度难度
编程语言Swift

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. TabView设计:使用TabView创建四个标签页,分别为“首页”“发现”“消息”“我的”。为每个标签页设置对应的视图。
  2. SplitView设计:在“发现”标签页中,使用SplitView实现左右分栏。左侧放置分类列表,右侧根据左侧选择的分类展示内容。
  3. 动画效果:对于右侧内容展示的淡入淡出动画,使用withAnimation结合opacity等修饰符实现。对于TabView切换的自定义过渡动画,通过tabViewStyle结合自定义的TabBarStyle实现。
  4. 多窗口支持:确保视图布局和交互在不同窗口环境下自适应,通过GeometryReader获取窗口大小等信息,进行布局调整。

涉及技术点

  1. SwiftUI基础组件TabViewSplitView
  2. 动画效果withAnimationopacitytabViewStyle
  3. 响应式布局GeometryReader
  4. 数据绑定@State@Binding

代码实现

import SwiftUI

struct Category: Identifiable {
    let id = UUID()
    let name: String
}

struct ContentView: View {
    @State private var selectedTab = 0
    @State private var selectedCategory: Category?
    let categories = [
        Category(name: "Category 1"),
        Category(name: "Category 2"),
        Category(name: "Category 3")
    ]
    
    var body: some View {
        TabView(selection: $selectedTab) {
            HomeView()
               .tabItem {
                    Image(systemName: "house")
                    Text("首页")
                }
               .tag(0)
            
            DiscoveryView(selectedCategory: $selectedCategory, categories: categories)
               .tabItem {
                    Image(systemName: "magnifyingglass")
                    Text("发现")
                }
               .tag(1)
            
            MessageView()
               .tabItem {
                    Image(systemName: "message")
                    Text("消息")
                }
               .tag(2)
            
            ProfileView()
               .tabItem {
                    Image(systemName: "person")
                    Text("我的")
                }
               .tag(3)
        }
       .tabViewStyle(CustomTabBarStyle())
    }
}

struct HomeView: View {
    var body: some View {
        Text("Home View")
    }
}

struct DiscoveryView: View {
    @Binding var selectedCategory: Category?
    let categories: [Category]
    
    var body: some View {
        SplitView {
            List(categories) { category in
                Button(action: {
                    withAnimation {
                        selectedCategory = category
                    }
                }) {
                    Text(category.name)
                }
            }
        } detail: {
            if let selectedCategory = selectedCategory {
                CategoryContentView(category: selectedCategory)
            } else {
                Text("Select a category")
            }
        }
    }
}

struct CategoryContentView: View {
    let category: Category
    
    var body: some View {
        withAnimation(.easeInOut(duration: 0.3)) {
            VStack {
                Text("Content for \(category.name)")
            }
           .opacity(1)
        }
    }
}

struct MessageView: View {
    var body: some View {
        Text("Message View")
    }
}

struct ProfileView: View {
    var body: some View {
        Text("Profile View")
    }
}

struct CustomTabBarStyle: TabBarStyle {
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            ForEach(configuration.tabs, id: \.title) { tab in
                VStack {
                    tab.icon
                    if configuration.selection?.title == tab.title {
                        Rectangle()
                           .fill(Color.blue)
                           .frame(height: 2)
                    } else {
                        Rectangle()
                           .fill(Color.clear)
                           .frame(height: 2)
                    }
                }
               .onTapGesture {
                    withAnimation(.easeInOut(duration: 0.3)) {
                        configuration.selection = tab.value
                    }
                }
            }
        }
       .padding(.horizontal)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}