美文网首页SwiftUI使用SwiftUI开发一个APP
使用SwiftUI开发一个APP - 页面跳转navigatio

使用SwiftUI开发一个APP - 页面跳转navigatio

作者: LazyGunner | 来源:发表于2021-08-27 10:47 被阅读0次

    之前的文章介绍了列表视图和无限加载瀑布流,今天的主要内容是讲在列表视图上方增加一个搜索输入框,并跳转到搜索页面完成搜索的实现。

    0. 调整文件夹结构

    因为后面多了一些页面,所以相比之前的所有文件都丢到一个文件夹的粗暴方式,还是更文明的把View和Model都整理到文件夹中比较好

    image

    1. 搜索框以及搜索页面跳转

    因为首页瀑布流和搜索结果的瀑布流中,资源列表的展示都是相同的。所以,我将之前的ResourceListView作为一个通用的瀑布流View放在了一个独立文件中,供HomeView 和 SearchView两个视同集成。

    首先我们来看一下HomeView的结构:

    //  HomeView.swift
    //  FoloPro
    //
    //  Created by GUNNER on 2021/8/15.
    //
    
    import SwiftUI
    
    struct HomeView: View {
        @StateObject var viewModel = ResourceViewModel()
        @State var searchText = ""
        @State var isEditing = false
        @State var isCommit = false
    
        var searchView = SearchView()
    
        var body: some View {
            NavigationView {
                VStack{
                    HStack{
                        TextField("搜索", text: $searchText, onCommit: {
                                print(searchText)
                                self.isCommit = true
                            })  // 1
                            .textFieldStyle(RoundedBorderTextFieldStyle())
                            .padding(7)
                            .padding(.horizontal, 25)
                            .background(Color(.systemGray6))
                            .cornerRadius(8)
                            .padding(.horizontal, 10)
                            .overlay(
                                HStack {
                                    Image(systemName: "magnifyingglass")
                                        .foregroundColor(.gray)
                                        .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
                                        .padding(.leading, 16)
    
                                    if isEditing {
                                        Button(action: {
                                            self.searchText = ""
                                        }) {
                                            Image(systemName: "multiply.circle.fill")
                                                .foregroundColor(.gray)
                                                .padding(.trailing, 16)
                                        }
                                    }
                                }
                            ) // 2
                            .onTapGesture {
                                self.isEditing = true
                            } // 3
                        NavigationLink(destination:SearchView(searchText: searchText), isActive: $isCommit) {
    
                        } // 8
                        if isEditing {
                            Button(action: {
                                self.isEditing = false
                                self.searchText = ""
                                // 关闭键盘
                                UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    
                            }) {
                                Text("取消")
                            }
                            .padding(.trailing, 20)
                            .transition(.move(edge: .trailing))
                            .animation(.default)
                        } // 4
                    }
    
                    ResourceListView(viewModel: viewModel) // 5
                }.navigationBarHidden(true) // 6 
                .onAppear() {
                    searchText = ""
                    isEditing = false
                } // 7
            }
        }
    }
    
    struct HomeView_Previews: PreviewProvider {
        static var previews: some View {
            HomeView()
        }
    }
    
    1. 通过 TextView作为搜索的输入框,绑定输入到searchText变量上,并设置一个onCommit事件的处理函数,这里用来跳转到搜索页面。

    2. 在输入框上叠加 搜索图标 和 清空图片按钮

    3. 点击事件,设置isEditing变量为 true, 用来展示 取消按钮

    4. 取消按钮

    5. 首页瀑布流

    6. 隐藏导航栏

    7. onAppear 生命周期,清空输入框内容 和 设置isEditing 为 false,主要用于从搜索页面返回时重置搜索框

    8. 通过NavigationLink完成页面跳转,变成完成页面跳转是通过 isActive参数来控制的,详见官方文档https://developer.apple.com/documentation/swiftui/navigationlink。同时我在跳转到SearchView的同时,通过SearchView的构造函数中的searchText参数完成 参数传递。

    看一下效果:

    image image

    接下来看一下 SearchView的代码

    //  SearchView.swift
    //  FoloPro
    //
    //  Created by GUNNER on 2021/8/15.
    //
    
    import SwiftUI
    
    struct SearchView: View {
        @State var searchText = ""
        @State var isEditing = false
        @State var isCommit = false
        @StateObject var viewModel = SearchResourceViewModel()
    
        var body: some View {
                VStack{
                    HStack{  // 1
                        TextField("搜索", text: $searchText, onCommit: {
                                viewModel.queryStr = searchText
                                viewModel.currentPage = 1
                                viewModel.getResourceList() // 2
                            }) 
                            .textFieldStyle(RoundedBorderTextFieldStyle())
                            .padding(7)
                            .padding(.horizontal, 25)
                            .background(Color(.systemGray6))
                            .cornerRadius(8)
                            .padding(.horizontal, 10)
                            .overlay(
                                HStack {
                                    Image(systemName: "magnifyingglass")
                                        .foregroundColor(.gray)
                                        .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
                                        .padding(.leading, 16)
    
                                    if isEditing {
                                        Button(action: {
                                            self.searchText = ""
                                        }) {
                                            Image(systemName: "multiply.circle.fill")
                                                .foregroundColor(.gray)
                                                .padding(.trailing, 16)
                                        }
                                    }
                                }
                            )
                            .onTapGesture {
                                self.isEditing = true
                            }
                        if isEditing {
                            Button(action: {
                                self.isEditing = false
                                self.searchText = ""
                                // 关闭键盘
                                UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    
                            }) {
                                Text("取消")
                            }
                            .padding(.trailing, 20)
                            .transition(.move(edge: .trailing))
                            .animation(.default)
                        }
                    }
    
                    ResourceListView(viewModel: viewModel)
                }.onAppear() {
                    viewModel.queryStr = searchText
                    viewModel.getResourceList()
                }
            }
    
    }
    
    struct SearchView_Previews: PreviewProvider {
        static var previews: some View {
            SearchView(searchText: "lost")
        }
    }
    
    1. 首先页面结构和 目前的HomeView一致,都是顶部一个搜索框,下面是瀑布流。
    2. 不一样的是在 页面刚一进入和输入框提交的时候,都调用了viewModel 的 getResourceList()方法。
    image

    2. StateObject 和 Protocol

    上文提到过,因为HomeView 和 SearchView 页面结构是相同的,所以就都集成了ResourceListView瀑布流视图。但是有一个问题是HomeView 请求的接口还有传参 和 SearchView是不同的,也就是初始化 ResourceListView(viewModel: viewModel) 中的viewModel是不同的。

    这里第一时间想到了,通过定义一个ResourceModel Protocol,让SearchResourceViewModel 和 ResourceViewModel 都实现这个Protocol,在ResourceListView中将 viewModel 的类型直接定义成 ResourceModel 这个Protocol的类型,就可以了。

    ResourceModelProtocol 文件:

    
    protocol ResourceModel: ObservableObject {
        var resourceList: [Resource] {
            get
            set 
        }
    
        func loadMoreContentIfNeeded(currentItem item: Resource?)
    }
    

    ResourceListView文件:

        @StateObject var viewModel: ResourceModel
        @Environment(\.colorScheme) var colorScheme
    

    但是遇到了一个报错: Protocol 'ResourceModel' as a type cannot conform to 'ObservableObject' . 看来不能直接这样用,通过网上搜索解决方案找到了大神的解答,将代码改为如下,就可以了。参考地址: https://stackoverflow.com/questions/59503399/how-to-define-a-protocol-as-a-type-for-a-observedobject-property

        @StateObject var viewModel: Model
    

    好了,页面跳转和搜索就好了。后续的功能要做底部导航栏、详情页和类目页了,敬请期待

    Tips:

    1. 折叠代码快捷键: CMD + Option + ← (折叠),CMD + Option + → (打开)

    相关文章

      网友评论

        本文标题:使用SwiftUI开发一个APP - 页面跳转navigatio

        本文链接:https://www.haomeiwen.com/subject/foeuiltx.html