前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用NavigationViewKit增强SwiftUI的导航视图

用NavigationViewKit增强SwiftUI的导航视图

作者头像
东坡肘子
发布2022-07-28 12:53:55
3.2K0
发布2022-07-28 12:53:55
举报

用NavigationViewKit增强SwiftUI的导航视图

如果想获得更好的阅读体验,请访问我的博客 www.fatbobman.com[1]

最近一直在为我的iOS健康管理app健康笔记3.0[2]做前期的准备工作。

由于SwiftUI原生提供的导航手段能力有限,因此在之前的版本中,NavigationView总是使用的不是那么的顺手。有以下几个我不满意的地方:

•缺少直接返回根视图的便捷手段•无法通过代码(不通过NavigationLink)跳转到新视图•双栏模式(DoubleColumnNavigationViewStyle)下显示风格不统一•在iPad下,无法在竖屏(Portrait)模式下保持双栏状态

因此,在这次开发的准备阶段,我写了一个针对NavigationView的扩展库——NavigationViewKit[3]。该扩展遵循以下几个原则:

•非破坏性任何新添加的功能都不能影响当前SwiftUI提供的原生功能,尤其是不能影响例如ToolbarNavigationLink在NavigationView中的表现•尽可能便于使用仅需极少的代码便可使用新增功能•SwiftUI原生风格扩展功能的调用方法尽可能同原生SwiftUI方式类似

请访问Github下载NavigationViewKit[4]

NavigationViewManager

简介

开发者对NavigationView最大抱怨之一就是不支持便捷的返回根视图手段。目前常用的解决方案有两种:

•重新包装UINavigationController好的包装确实可以使用到UINavigationController提供的众多功能,不过非常容易同SwiftUI中的原生方法相冲突,鱼和熊掌不可兼得•使用程序化的NavigationLink通过撤销根视图的程序化的NavigationLink(通常是isActive)来返回。此种手段将限制NavigationLink的种类选择,另外不利于从非视图代码中实现。

NavigationViewManager是NavigationViewKit中提供的导航视图管理器,它提供如下功能:

•可以管理应用程序中全部的NavigationView•支持从NavigationView下的任意视图通过代码直接返回根视图•在NavigationView下的任意视图中通过代码直接跳转到新视图(无需在视图中描述NavigationLink)•通过NotificatiionCenter,指定应用程序中的任意NavigationView返回根视图•通过NotificatiionCenter,让应用程序中任意的NavigationView跳转到新视图•支持转场动画的开启关闭

注册NavigationView

由于NavigationgViewManager支持多导航视图管理,因此需要为每个受管理的导航视图进行注册。

代码语言:javascript
复制
import NavigationViewKitNavigationView {            List(0..<10) { _ in                NavigationLink("abc", destination: DetailView())            }        }        .navigationViewManager(for: "nv1", afterBackDo: {print("back to root") })

navigationViewManager是一个View扩展,定义如下:

代码语言:javascript
复制
extension View {    public func navigationViewManager(for tag: String, afterBackDo cleanAction: @escaping () -> Void = {}) -> some View}

for为当前注册的NavigationView的名称(或tag),afterBackDo为当转到根视图后执行的代码段。

应用程序中每个被管理的NavigationView的tag需唯一。

从视图中返回根视图

在注册过的NavigationView的任意子视图中,可以通过下面的代码实现返回根视图:

代码语言:javascript
复制
@Environment(\.navigationManager) var nvmanager         Button("back to root view") {    nvmanager.wrappedValue.popToRoot(tag:"nv1"){               print("other back")           }}

popToRoot定义如下:

代码语言:javascript
复制
func popToRoot(tag: String, animated: Bool = true, action: @escaping () -> Void = {})

tag为当前NavigationView的注册Tag,animated设置返回根视图时是否显示转场动画,action为进一步的善后代码段。该段代码将执行在注册代码段(afterBackDo)之后,主要用于传递当前视图中的数据。

可以通过

代码语言:javascript
复制
@Environment(\.currentNaviationViewName) var tag

获取到当前NavigationView的注册Tag,便于视图在不同的NavigtionView中复用

代码语言:javascript
复制
struct DetailView: View {    @Environment(\.navigationManager) var nvmanager    @Environment(\.currentNaviationViewName) var tag    var body: some View {        VStack {            Button("back to root view") {                if let tag = tag {                    nvmanager.wrappedValue.popToRoot(tag:tag,animated: false) {                        print("other back")                    }                }            }        }    }}

使用NotificationCenter返回根视图

由于NavigationViewManager在我的app中主要的用途是处理Deep Link,绝大多数的时间都不是在视图代码中调用的。因此NavigationViewManager提供了基于NotificationCenter的类似方法。

在代码中使用:

代码语言:javascript
复制
let backToRootItem = NavigationViewManager.BackToRootItem(tag: "nv1", animated: false, action: {})NotificationCenter.default.post(name: .NavigationViewManagerBackToRoot, object: backToRootItem)

让指定的NavigationView返回到根视图。

演示如下:

backToRootDemo

从视图中跳转到新视图

在视图代码中使用:

代码语言:javascript
复制
@Environment(\.navigationManager) var nvmanagerButton("go to new View"){        nvmanager.wrappedValue.pushView(tag:"nv1",animated: true){            Text("New View")                .navigationTitle("new view")        }}

pushView的定义如下:

代码语言:javascript
复制
func pushView<V: View>(tag: String, animated: Bool = true, @ViewBuilder view: () -> V)

tag为NavigationView的注册Tag,animation设置是否显示转场动画,view为新视图。视图中支持SwiftUI原生的所有定义,例如toolbarnavigationTitle等。

目前在启用转场动画时,title和toolbar会在转场动画后才显示,观感稍有不足。日后尝试解决。

使用NotificationCenter跳转到新视图

在代码中:

代码语言:javascript
复制
let pushViewItem = NavigationViewManager.PushViewItem(tag: "nv1", animated: false) {                    AnyView(                        Text("New View")                            .navigationTitle("第四级视图")                    )                }NotificationCenter.default.post(name:.NavigationViewManagerPushView, object: pushViewItem)

通过NotificationCenter跳转视图时,视图需转换为AnyView

演示:

pushNewView

DoubleColoumnJustForPadNavigationViewStyle

DoubleColoumnJustForPadNavigationViewStyleDoubleColoumnNavigationViewStyle的修改版,其目的是改善当iPhone和iPad使用同一套代码时,DoubleColoumnNavigationViewStyle在iPhone Max上横屏时的表现同其他iPhone机型不同。

当iPhone Max横屏时,NavigationView的表现会同iPad一样双列显示,让应用程序在不同iPhone上的表现不一致。

使用DoubleColoumnJustForPadNavigationViewStyle时,iPhone Max在横屏时仍呈现StackNavigationViewStyle的式样。

使用方法:

代码语言:javascript
复制
NavigationView{   ...}.navigationViewStyle(DoubleColoumnJustForPadNavigationViewStyle())

在swift 5.5下可以直接使用

代码语言:javascript
复制
.navigationViewStyle(.columnsForPad)

TipOnceDoubleColumnNavigationViewStyle

当前DoubleColumnNavigationViewStyle在iPad上横竖屏的表现不同。当竖屏时,左侧栏默认会隐藏,容易让新用户无所适从。

TipOnceDoubleColumnNavigationViewStyle会在iPad首次进入竖屏状态时,将左侧栏显示在右侧栏上方,提醒使用者。该提醒只会进行一次。提醒后旋转了方向,再次进入竖屏状态则不会二次触发提醒。

代码语言:javascript
复制
NavigationView{   ...}.navigationViewStyle(TipOnceDoubleColumnNavigationViewStyle())

在Swift 5.5下可以直接使用

代码语言:javascript
复制
.navigationViewStyle(.tipColumns)

演示:

TipOnceDoubleColumnNavigationViewStyleDemo

FixDoubleColumnNavigationViewStyle

在健康笔记[5]中,我希望iPad版本无论在横屏或竖屏时,都始终能够保持两栏显示的状态,且左侧栏不可隐藏。

我之前使用了HStack套两个NavigationView来达到这个效果:

image-20210831194932840

现在,可以直接NavigationViewKit中的FixDoubleColumnNavigationViewStyle轻松实现上述效果。

代码语言:javascript
复制
NavigationView{   ...}.navigationViewStyle(FixDoubleColumnNavigationViewStyle(widthForLandscape: 350, widthForPortrait:250))

并且可以为横屏竖屏两种状态分别设置左侧栏宽度。

演示:

FixDoubleColumnNavigationViewStyleDemo

总结

NavigationViewKit目前功能还比较少,我会根据自己的使用需要,逐步增加新的功能。

如果你在使用中发现问题或者有其他需求,请在Github上提交Issue或在我的博客中留言。

请访问Github下载NavigationViewKit[6]

引用链接

[1] www.fatbobman.com: http://www.fatbobman.com [2] 健康笔记3.0: https://www.fatbobman.com/healthnotes/ [3] NavigationViewKit: https://github.com/fatbobman/NavigationViewKit [4] NavigationViewKit: https://github.com/fatbobman/NavigationViewKit [5] 健康笔记: https://www.fatbobman.com/healthnotes/ [6] NavigationViewKit: https://github.com/fatbobman/NavigationViewKit

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 肘子的Swift记事本 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 用NavigationViewKit增强SwiftUI的导航视图
    • NavigationViewManager
      • 简介
      • 注册NavigationView
      • 从视图中返回根视图
      • 使用NotificationCenter返回根视图
      • 从视图中跳转到新视图
      • 使用NotificationCenter跳转到新视图
    • DoubleColoumnJustForPadNavigationViewStyle
      • TipOnceDoubleColumnNavigationViewStyle
        • FixDoubleColumnNavigationViewStyle
          • 总结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档