首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

SwiftUI onAppear在NavigationView inside TabView时调用了两次

基础概念

SwiftUI 是苹果推出的一个声明式 UI 框架,用于构建 macOS、iOS、watchOS 和 tvOS 应用程序。onAppear 是 SwiftUI 中的一个修饰符,用于在视图即将显示时执行某些操作。

NavigationView 是 SwiftUI 中的一个容器视图,用于管理导航链接和堆栈。TabView 则是一个容器视图,允许用户在多个视图之间切换。

问题描述

NavigationView 放在 TabView 内部时,onAppear 可能会被调用两次。这是因为 TabView 在切换标签时会重新加载其子视图,而 NavigationView 在进入新的导航链接时也会重新加载其子视图。

原因分析

  1. TabView 切换标签:当用户切换 TabView 中的标签时,TabView 会重新加载其子视图,这会导致 NavigationView 和其子视图的 onAppear 被调用。
  2. NavigationView 导航:当用户在 NavigationView 中导航到新的链接时,NavigationView 会重新加载其子视图,这也会导致 onAppear 被调用。

解决方案

为了避免 onAppear 被多次调用,可以使用 @StateObject@ObservedObject 来管理视图的状态,并在状态变化时执行操作。

示例代码

代码语言:txt
复制
import SwiftUI

struct ContentView: View {
    @StateObject private var viewModel = ViewModel()

    var body: some View {
        TabView {
            NavigationView {
                Text("Tab 1")
                    .onAppear {
                        viewModel.didAppear()
                    }
                    .navigationTitle("Tab 1")
            }
            .tag(0)

            NavigationView {
                Text("Tab 2")
                    .onAppear {
                        viewModel.didAppear()
                    }
                    .navigationTitle("Tab 2")
            }
            .tag(1)
        }
    }
}

class ViewModel: ObservableObject {
    @Published var appeared = false

    func didAppear() {
        if !appeared {
            appeared = true
            print("onAppear called")
        }
    }
}

参考链接

SwiftUI onAppear 被调用多次的问题

应用场景

这个问题常见于需要在 NavigationViewTabView 中执行某些初始化操作或数据加载操作的场景。通过上述解决方案,可以确保这些操作只在视图首次显示时执行一次。

优势

  1. 避免重复操作:通过状态管理,可以避免在视图多次显示时重复执行某些操作。
  2. 提高性能:减少不必要的操作可以提高应用的性能和响应速度。
  3. 代码清晰:使用状态管理可以使代码逻辑更加清晰和易于维护。

希望这个回答能帮助你理解 SwiftUIonAppear 被调用两次的原因,并提供有效的解决方案。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

SwiftUI 视图的生命周期研究

NavigationView 中,如果在 NavigationLink 中使用了静态的目标视图,SwiftUI 将会为所有的目标视图创建实例,无论是否访问。...• TabView 中,SwiftUI 一开始就为所有 tab 对应的视图创建了实例。 类似上面的情况还有不少。这也就很好的解释了,很多开发者都会碰到某些视图莫名多次初始化的情况。...: "2") .tag(2)} SwiftUI 将只最初创建两个 ShowMessage 的实例,无论如何切换 selection,TabView 将全程只使用这两个实例。...SwiftUI 官方文档对 onAppear 和 onDisappear 的描述是:在此视图出现时执行的操作,在此视图消失时要执行的操作。这种描述与这两个修饰器大多数场景下的行为很接近。...,比如 TabView、或者将 frame 设置为 zero 等等。

4.4K30

掌握 SwiftUI 的 Safe Area

UIKit 中,开发者需要利用 safeAreaInsets 或 safeAreaLayoutGuide ,才能确保将视图放置界面中的可见部分。 SwiftUI 对上述过程进行了彻底的简化。... SwiftUI 中,开发者通常只有需要获取 StatusBar + NavBar 的高度或 HomeIndeicator + TabBar 的高度时才会使用到 safeAreaInsets 。...List(0..<100){ id in Text("id\(id)")} safeAreInsetList1 当被嵌入到 TabView 时,TabView 会调整其内部的安全区域。...safeAreaInsetList2 遗憾的是, iOS 15 之前,SwiftUI 并没有提供调整视图安全区的手段,如果我们想通过 SwiftUI 的手段创建一个自定义 Tabbar 时,列表中最后的内容将被...尽管使用 safeAreaInset 为列表底部添加状态栏或自定义 TabBar 非常方便,但如果你的列表中使用了 TextField,情况将变得很麻烦。

7.7K31
  • 从用SwiftUI搭建项目说起

    Apple SwiftUI SwiftUI ---- 进入项目搭建先说说我自己对SwiftUI的一个基本的认知: SwiftUI我觉得对iOSer来说最大的是开发UI模式的优化...UIkit那样去创建Controller来管理View,SwiftUI中最常见的就是View。...UIKit中我们的导航、标签都是通过控制器来管理,但是SwiftUI中他们分别是通过NavigationView+TabView管理的,我们得认识上有一个基本的转变,从Controller到View...认识一下NavigationView,先看看下面的代码: NavigationView{ NavigationLink.init( destination...public typealias Body = some View } 关于这个TabView定义的上面苹果是给出了一个使用的基本的示例的,要和我们项目中经常使用的模式要绑定在一起的的话就是结合他的初始化方法绑定一个

    4.5K20

    SwiftUI Release 引入的辅助焦点管理

    这个新功能使得SwiftUI中处理辅助技术(如 VoiceOver 和 Switch Control)的焦点状态变得更加轻松。...本文将介绍如何使用 @FocusState 属性包装器来SwiftUI中管理和移动辅助焦点。...使用 @FocusState 属性包装器 SwiftUI Release 中,我们获得了一整套特殊工具来更有效地处理辅助焦点。...通常,屏幕上有多个元素,您可能希望它们之间移动焦点。为了支持这种情况,SwiftUI 提供了一种通过枚举定义可聚焦字段并在它们之间切换的方法。...最后,我们提供了一些优化 SwiftUI 应用的建议,以更好地整合焦点管理,并通过最佳实践和总结使读者更深入地了解了 SwiftUI Release 中使用 @FocusState 管理焦点的方法。

    11510

    解析SwiftUI布局细节(二)循环轮播+复杂布局

    + NavigationLink 的界面跳转,苹果给的 SwiftUI 的使用例子中就是这样写的,当然我们正常的使用中这样写也没啥问题,那我们界面跳转的问题是什么呢?...如果你看了我们 Demo中的代码,你就知道我们是采用 TabView 嵌套 NavigationView 的形式,在这样的模式下似乎是存在问题的, TabView+NavigationView 中你利用...传送门在这 下面是我们值得细说的一些点: 1、值得注意的 TabView + PageTabViewStyle 这是iOS14中新出的一个值得我们注意的点,PageTabViewStyle... iOS 13.5 中,内容放置方式为 .center。 iOS 14.0 中则为:.topLeading。...3、再提一点关于上面说的滚动视图,UIKit中我们可以用UICollectionView搞定一切,但是SwiftUI中没有这个控件,我建议采用的方式是 ScrollView + HStack + VStack

    12.1K20

    打造可适配多平台的 SwiftUI 应用

    SwiftUI 不同平台中的“限制”( 每个平台的特点、优势、处理方式 )有了比较清晰的认识。...图片由于“电影猎手”采用了编程式导航,视图堆栈以及 TabView 的状态都保存在 Store 中,因此会出现操作同步的情况。...它只有一个 Store 实例并支持多窗口,使用者每个窗口中都可以独立地切换 TabView,并且 TabView 的状态由唯一的 Store 实例持有。...当一个场景被创建后,通过 onAppear 里的代码, App State 中创建属于它自己的 State 数据,并在场景被删除时,通过 onDisappear 里的代码,将当前场景的 State 清除掉...为了避免这种情况,需要在 onAppear 中重新生成新的 UUID 或随机数。.

    3.2K80

    老人新兵 —— 一款 iOS APP 的开发手记

    TabView 切换时,应该是把原来的 view 完全销毁掉,而且销毁的效率很低。导致如果页面复杂切换就像机械相机按动快门一样,会闪。...当然也有弊端,使用了 ZStack 后,所有的 view 即使看不见实际上也已经初始化并显示了,并且不会销毁,因此失去通过 onAppear 和 onDisappear 进行介入的手段( 最后采用了其他的变通方式...我的 app 中,多数情况它还是胜任的,不过它和某些 UIKit 的实现结合起来使用会出现灵异现象,最后个别页面中,还是使用了 UIScrollView 才解决了问题。...NavigationView灵异现象较多。...感觉 SwiftUI 销毁 view 上的代码有比较严重的效率 bug( 参见上面的 TabView )。

    2.5K40

    SwiftUI + Core Data App 的内存占用优化之旅

    第一轮优化:对视图 body 值进行优化 第一轮优化中,我们会首先尝试从 SwiftUI 的角度入手。...当子视图进入惰性容器的可视区域时,SwiftUI 会调用它的 onAppear 闭包,子视图退出可视区域时,会调用 onDisappear 闭包。...我们可以通过 onAppear 以及 onDisappear 中对图片的显示与否( 变量 show )进行控制( 迫使 SwiftUI 对视图的 body 重新求值 ),从而减少因上述原因所增加的内存占用...中 视图显示该 Image onAppear 闭包运行结束时,Picture 对象将自动被释放 onDisapper 中清除 Source of truth 中的内容( 设置为 nil ) 按照预想...即使我们 onDisappear 中将该变量设置为 nil,但 SwiftUI 并没有释放之前它所占用的空间。

    1.3K10

    SwiftUI + Core Data App 的内存占用优化之旅

    第一轮优化:对视图 body 值进行优化 第一轮优化中,我们会首先尝试从 SwiftUI 的角度入手。...当子视图进入惰性容器的可视区域时,SwiftUI 会调用它的 onAppear 闭包,子视图退出可视区域时,会调用 onDisappear 闭包。...我们可以通过 onAppear 以及 onDisappear 中对图片的显示与否( 变量 show )进行控制( 迫使 SwiftUI 对视图的 body 重新求值 ),从而减少因上述原因所增加的内存占用...中 视图显示该 Image onAppear 闭包运行结束时,Picture 对象将自动被释放 onDisapper 中清除 Source of truth 中的内容( 设置为 nil ) 按照预想...即使我们 onDisappear 中将该变量设置为 nil,但 SwiftUI 并没有释放之前它所占用的空间。

    2.4K40

    SwiftUI 与 Core Data —— 安全地响应数据

    SwiftUI 与 Core Data —— 问题[1]SwiftUI 与 Core Data —— 数据定义[2]SwiftUI 与 Core Data —— 数据获取[3]可以 此处[4] 获取演示项目...Core Data 受限于 Objective-C 中可表达的类型限制,在即使使用了标量转换的情况下( Scalar )也不具备与 Swift 原生类型对应的能力。..., formatter: itemFormatter)") .onAppear{ // 进入 NavigationLink 后一秒钟删除该数据...由于 Core Data 模版代码中,只使用了一行代码来声明次级视图:Text("Item at \(item.timestamp!...在上节的演示中,当数据被删除后( 通过 onAppear 闭包中的延迟操作 ),NavigationView 会自动返回到根视图中。在这种情况下,持有该数据的视图将伴随着数据删除一并消失。

    3.3K20

    用NavigationViewKit增强SwiftUI的导航视图

    由于SwiftUI原生提供的导航手段能力有限,因此之前的版本中,NavigationView总是使用的不是那么的顺手。...该扩展遵循以下几个原则: •非破坏性任何新添加的功能都不能影响当前SwiftUI提供的原生功能,尤其是不能影响例如Toolbar、NavigationLinkNavigationView中的表现•尽可能便于使用仅需极少的代码便可使用新增功能...•SwiftUI原生风格扩展功能的调用方法尽可能同原生SwiftUI方式类似 请访问Github下载NavigationViewKit[4] NavigationViewManager 简介 开发者对NavigationView...视图中支持SwiftUI原生的所有定义,例如toolbar、navigationTitle等。 目前启用转场动画时,title和toolbar会在转场动画后才显示,观感稍有不足。日后尝试解决。...我之前使用了HStack套两个NavigationView来达到这个效果: image-20210831194932840 现在,可以直接NavigationViewKit中的FixDoubleColumnNavigationViewStyle

    3.2K20

    掌握 SwiftUI 的 task 修饰器

    欢迎大家 Discord 频道[2] 中进行更多地交流随着 Swift 5.5 引入了 async/await 特性,苹果也为 SwiftUI 添加了 task 视图修饰器,以方便开发者视图中使用基于...用 “出现之前” 来描述 onAppear 或 task 闭包的调用时机属于无奈之举。不同的上下文中,“出现之前”会有不同的解释。...详情请参阅 SwiftUI 视图的生命周期研究[3] 一文中有关 onAppear 和 onDisappear 的章节SwiftUI 为了判断视图的状态是否发生了改变,它会在视图的存续期内,反复地生成视图类型实例以达成此目的...因此,开发者应避免将一些会对性能造成影响的操作放置视图类型的构造函数之中,而是 onAppear 或 task 中进行该类型的操作。...了解了两个版本的 task 修饰器的工作原理和调用机制后,为老版本的 SwiftUI 添加 task 修饰器将不再有任何困难。

    2.2K30

    掌握 SwiftUI 的 task 修饰器

    原文发表于我的博客 肘子的Swift记事本 task vs onAppear SwiftUI 提供了两个版本的 task 修饰器,版本一的作用和调用时机与 onAppear 十分类似: public func...用 “出现之前” 来描述 onAppear 或 task 闭包的调用时机属于无奈之举。不同的上下文中,“出现之前”会有不同的解释。...详情请参阅 SwiftUI 视图的生命周期研究 一文中有关 onAppear 和 onDisappear 的章节 SwiftUI 为了判断视图的状态是否发生了改变,它会在视图的存续期内,反复地生成视图类型实例以达成此目的...因此,开发者应避免将一些会对性能造成影响的操作放置视图类型的构造函数之中,而是 onAppear 或 task 中进行该类型的操作。...了解了两个版本的 task 修饰器的工作原理和调用机制后,为老版本的 SwiftUI 添加 task 修饰器将不再有任何困难。

    3.6K60

    SwiftUI 动画进阶 — Part4:TimelineView

    前言 前三篇高级 SwiftUI 动画系列是作者 WWDC 2021 之前实战总结的内容。对 2021 年 WWDC 介绍的 TimelineView 和 Canvas 感到激动。...这最终将让我们SwiftUI 中创建我们自己的类似关键帧的动画。 但是让我们慢慢开始,从我们的小项目开始:如下所示的节拍器。调高音量播放视频,欣赏节拍声如何与钟摆同步。...除了每次日期值更改时推进动画阶段,我们还在 onAppear 闭包中执行此操作。否则,一开始就会有停顿。 最后一段与 SwiftUI 无关的代码是创建 NSSound 实例。...关键帧动画 心脏和节拍器示例某种程度上是关键帧动画。我们整个动画中定义了几个关键点,在这里我们改变了我们视图的参数,并让 SwiftUI 动画这些点之间的过渡。...如果在某个时候,你想要/需要告诉你的视图刷新,你可以随时调用 objectWillChange.send() 匹配动画持续时间和偏移量:关键帧示例中,我们为每个动画片段使用不同的动画。

    3.8K30
    领券