如何在 SwiftUI 中使视图的大小与另一个视图的大小相同

2023-12-29

我正在尝试重新创建 Twitter iOS 应用程序的一部分来学习 SwiftUI,并且想知道如何动态地将一个视图的宽度更改为另一个视图的宽度。就我而言,使下划线与文本视图的宽度相同。

我附上了一张屏幕截图,以尝试更好地解释我所指的内容。任何帮助将不胜感激,谢谢!

这也是我到目前为止的代码:

import SwiftUI

struct GridViewHeader : View {

    @State var leftPadding: Length = 0.0
    @State var underLineWidth: Length = 100

    var body: some View {
        return VStack {
            HStack {
                Text("Tweets")
                    .tapAction {
                        self.leftPadding = 0

                }
                Spacer()
                Text("Tweets & Replies")
                    .tapAction {
                        self.leftPadding = 100
                    }
                Spacer()
                Text("Media")
                    .tapAction {
                        self.leftPadding = 200
                }
                Spacer()
                Text("Likes")
            }
            .frame(height: 50)
            .padding(.horizontal, 10)
            HStack {
                Rectangle()
                    .frame(width: self.underLineWidth, height: 2, alignment: .bottom)
                    .padding(.leading, leftPadding)
                    .animation(.basic())
                Spacer()
            }
        }
    }
}

我已经写了关于使用 GeometryReader、视图首选项和锚点首选项的详细说明。下面的代码使用了这些概念。有关它们如何工作的更多信息,请查看我发布的这篇文章:https://swiftui-lab.com/communicating-with-the-view-tree-part-1/ https://swiftui-lab.com/communicating-with-the-view-tree-part-1/

下面的解决方案将正确设置下划线的动画:

我努力完成这项工作,我同意你的观点。有时,您只需要能够在层次结构中向上或向下传递一些框架信息。事实上,WWDC2019 会议 237(使用 SwiftUI 构建自定义视图)解释了视图不断传达其大小。它基本上是说父母向孩子建议尺寸,孩子决定他们想要如何布局自己并与父母沟通。他们是怎么做到的?我怀疑anchorPreference与此有关。然而,它非常模糊并且根本没有记录。 API 是公开的,但是要掌握那些长函数原型是如何工作的……我现在没有时间。

我认为 Apple 没有记录这一点是为了迫使我们重新思考整个框架,忘记“旧的”UIKit 习惯并开始声明式思考。然而,有时仍然需要这样做。您有没有想过背景修改器是如何工作的?我很想看到这个实施。它会解释很多!我希望苹果能在不久的将来记录偏好。我一直在尝试自定义 PreferenceKey,它看起来很有趣。

现在回到您的具体需求,我设法解决了。您需要两个尺寸(文本的 x 位置和宽度)。一个我觉得很公平,另一个似乎有点黑客。尽管如此,它工作得很好。

文本的 x 位置我通过创建自定义水平对齐方式解决了这个问题。有关该检查会话 237 的更多信息(19:00 分钟)。尽管我建议您观看整个内容,但它可以让您了解布局过程的工作原理。

然而,我并不为宽度感到自豪......;-) 它需要 DispatchQueue 以避免在显示时更新视图。更新:我在下面的第二个实现中修复了它

首次实施

extension HorizontalAlignment {
    private enum UnderlineLeading: AlignmentID {
        static func defaultValue(in d: ViewDimensions) -> CGFloat {
            return d[.leading]
        }
    }

    static let underlineLeading = HorizontalAlignment(UnderlineLeading.self)
}


struct GridViewHeader : View {

    @State private var activeIdx: Int = 0
    @State private var w: [CGFloat] = [0, 0, 0, 0]

    var body: some View {
        return VStack(alignment: .underlineLeading) {
            HStack {
                Text("Tweets").modifier(MagicStuff(activeIdx: $activeIdx, widths: $w, idx: 0))
                Spacer()
                Text("Tweets & Replies").modifier(MagicStuff(activeIdx: $activeIdx, widths: $w, idx: 1))
                Spacer()
                Text("Media").modifier(MagicStuff(activeIdx: $activeIdx, widths: $w, idx: 2))
                Spacer()
                Text("Likes").modifier(MagicStuff(activeIdx: $activeIdx, widths: $w, idx: 3))
                }
                .frame(height: 50)
                .padding(.horizontal, 10)
            Rectangle()
                .alignmentGuide(.underlineLeading) { d in d[.leading]  }
                .frame(width: w[activeIdx],  height: 2)
                .animation(.linear)
        }
    }
}

struct MagicStuff: ViewModifier {
    @Binding var activeIdx: Int
    @Binding var widths: [CGFloat]
    let idx: Int

    func body(content: Content) -> some View {
        Group {
            if activeIdx == idx {
                content.alignmentGuide(.underlineLeading) { d in
                    DispatchQueue.main.async { self.widths[self.idx] = d.width }

                    return d[.leading]
                }.onTapGesture { self.activeIdx = self.idx }

            } else {
                content.onTapGesture { self.activeIdx = self.idx }
            }
        }
    }
}

更新:不使用 DispatchQueue 更好的实现

我的第一个解决方案有效,但我对宽度传递到下划线视图的方式并不太自豪。

我找到了一种更好的方法来实现同样的目标。事实证明,背景修改器非常强大。它不仅仅是一个可以让您装饰视图背景的修改器。

基本步骤是:

  1. Use Text("text").background(TextGeometry())。 TextGeometry 是一个自定义视图,其父级与文本视图大小相同。这就是 .background() 的作用。很强大。
  2. 在我的实施中文本几何我使用 GeometryReader 来获取父级的几何图形,这意味着我获取了 Text 视图的几何图形,这意味着我现在有了宽度。
  3. 现在要传递宽度回来,我正在使用优先。关于它们的文档为零,但经过一些实验后,我认为如果您愿意的话,首选项类似于“查看属性”。我创建了我的自定义偏好键,称为宽度偏好键我在 TextGeometry 中使用它来将宽度“附加”到视图,以便可以在层次结构中更高的位置读取它。
  4. 回到祖先,我使用偏好改变检测宽度的变化,并相应地设置宽度数组。

这听起来可能太复杂了,但代码最好地说明了这一点。这是新的实现:

import SwiftUI

extension HorizontalAlignment {
    private enum UnderlineLeading: AlignmentID {
        static func defaultValue(in d: ViewDimensions) -> CGFloat {
            return d[.leading]
        }
    }

    static let underlineLeading = HorizontalAlignment(UnderlineLeading.self)
}

struct WidthPreferenceKey: PreferenceKey {
    static var defaultValue = CGFloat(0)

    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
    }

    typealias Value = CGFloat
}


struct GridViewHeader : View {

    @State private var activeIdx: Int = 0
    @State private var w: [CGFloat] = [0, 0, 0, 0]

    var body: some View {
        return VStack(alignment: .underlineLeading) {
            HStack {
                Text("Tweets")
                    .modifier(MagicStuff(activeIdx: $activeIdx, idx: 0))
                    .background(TextGeometry())
                    .onPreferenceChange(WidthPreferenceKey.self, perform: { self.w[0] = $0 })

                Spacer()

                Text("Tweets & Replies")
                    .modifier(MagicStuff(activeIdx: $activeIdx, idx: 1))
                    .background(TextGeometry())
                    .onPreferenceChange(WidthPreferenceKey.self, perform: { self.w[1] = $0 })

                Spacer()

                Text("Media")
                    .modifier(MagicStuff(activeIdx: $activeIdx, idx: 2))
                    .background(TextGeometry())
                    .onPreferenceChange(WidthPreferenceKey.self, perform: { self.w[2] = $0 })

                Spacer()

                Text("Likes")
                    .modifier(MagicStuff(activeIdx: $activeIdx, idx: 3))
                    .background(TextGeometry())
                    .onPreferenceChange(WidthPreferenceKey.self, perform: { self.w[3] = $0 })

                }
                .frame(height: 50)
                .padding(.horizontal, 10)
            Rectangle()
                .alignmentGuide(.underlineLeading) { d in d[.leading]  }
                .frame(width: w[activeIdx],  height: 2)
                .animation(.linear)
        }
    }
}

struct TextGeometry: View {
    var body: some View {
        GeometryReader { geometry in
            return Rectangle().fill(Color.clear).preference(key: WidthPreferenceKey.self, value: geometry.size.width)
        }
    }
}

struct MagicStuff: ViewModifier {
    @Binding var activeIdx: Int
    let idx: Int

    func body(content: Content) -> some View {
        Group {
            if activeIdx == idx {
                content.alignmentGuide(.underlineLeading) { d in
                    return d[.leading]
                }.onTapGesture { self.activeIdx = self.idx }

            } else {
                content.onTapGesture { self.activeIdx = self.idx }
            }
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 SwiftUI 中使视图的大小与另一个视图的大小相同 的相关文章

  • 将 Firebase 云消息传递与 Windows 应用程序结合使用

    我在 Android 和 iOS 应用程序中使用 Firebase Cloud Messaging 但是我还有此应用程序的 Windows Mac OS 版本 我想保留相同的逻辑 我知道 Firebase Cloud Messaging 可
  • 推送动画,没有阴影和停电

    我有一个简单的iOS NavigationController基于应用程序 二UICollectionViews 相继 如果元素打开 第一个合集 被点击时 第二集 将被打开 非常简单 重要的提示 Both UICollectionViews
  • AVAssetExportSession 为零 iPhone 7 - Plus 模拟器

    AVAssetExportSession在 iPhone 6 及以下版本上运行良好 但在 iPhone 7 iPhone 7 Plus 模拟器上运行不佳 Xcode 8 0 这段代码return nil在exportSession中 当在i
  • 根据 iOS 版本使用不同的类实现?

    iOS 11 最近添加了一个我想使用的新功能 但我仍然需要支持旧版本的 iOS 有没有一种方法可以将同一个类编写两次 并让较新版本的 iOS 使用该类的一个版本 而旧版本的 iOS 使用另一个版本 注 最初我用的是if available
  • Swift 中的 import 语句是否有相关成本?

    阅读字符串宣言 我看到一个段落 https github com apple swift blob master docs StringManifesto md batteries included关于避免Foundation不需要的时候导
  • UIView晃动动画

    我试图在按下按钮时使 UIView 摇动 我正在调整我找到的代码http www cimgf com 2008 02 27 core animation tutorial window shake effect http www cimgf
  • iOS - UITableViewCell 使文本加粗

    我有一个字符串 NSString userInfo James Johnson james 我想做的就是大胆James Johnson并保留 james正常字体 所以我尝试过的是使用NSAttributedString但为了完成这个过程 我
  • 如何动态获取 UITableViewCell 的高度

    我创建了自定义的tableViewCell 我在UITableViewCell中添加了UIView SubView 所以我在 UIView 中的所有动态文本和图像内容都会根据文本和图像大小而变化 但现在 HeightforRowAtInde
  • 如何使用 CNContacts 快速获取手机号码?

    我有一些代码可以检索用户联系人中的所有电话号码 但只想过滤掉手机号码 目前 我只是通过将第一个数字为 或第二个数字为 7 的数字添加到数组中来实现此目的 如下所示 func findContacts gt CNContact let key
  • Flutter 应用程序在 iOS 平台的 firebase 电话身份验证中崩溃

    我在我的项目中实现了 Firebase Phone auth 在 Android 端 一切正常 但对于 iOS 端 当我尝试从我的端发送验证码时 应用程序崩溃并失去连接 我已在下面提交了我的代码 主程序 dart class MyApp e
  • iPhone X 将对象底部与安全区域对齐会破坏其他设备上的外观

    关于 iPhone X 自动布局怪癖的问题 我有两个按钮 以前这些按钮将与超级视图底部对齐 偏移量为 20 以免它们接触屏幕底部 此后我将链接更改为安全区域而不是超级视图 Here s the original setup Looks go
  • 在 iOS 7 中 viewForHeaderInSection 部分是从 1 开始而不是从 0 开始

    我正在处理UITableView在我的项目中 这个项目是在 Xcode 4 5 中创建的 现在我正在使用 Xcode 5 所以我的问题是何时在 iOS 6 中运行我的项目 viewForHeaderInSection方法部分从 0 开始没问
  • 如何在 iPhone 上使用 SwiftUI 显示表格数据?

    我正在为当地的体育联盟开发一个应用程序 一个视图将是当前排名 每行有几个字段 球队名称 已玩的比赛 得分等 我希望球队列左对齐 其他列右对齐 似乎最好的答案是 SwiftUI 的 Table 但在 iPhone 上 它只显示第一列 我尝试过
  • UIViewController 不旋转到横向

    在许多情况下需要旋转控制器但不起作用 现在我遇到了相反的问题 它正在旋转 我想禁用它 在那个 ViewController 中我有这个 BOOL shouldAutorotateToInterfaceOrientation UIInterf
  • 未知异常和崩溃

    当我尝试快速滚动表格视图或从远程重新加载数据时 我的应用程序崩溃了 当我先进行远程获取然后滚动表格视图时 一切似乎都工作正常 我不知道下面的崩溃日志意味着什么 它只是有时工作正常 有时崩溃 Incident Identifier 710A1
  • 使用 UITabBarController 时覆盖整个屏幕的视图?

    我想在 UITabBarController 设置中在整个屏幕上覆盖 HUD 样式的透明图形 执行此操作的按钮位于第一个选项卡的屏幕 FirstViewController 中 并且覆盖层也应该覆盖选项卡 这可能吗 您可以将新视图直接附加到
  • GLKit的GLKMatrix“列专业”如何?

    前提A 当谈论线性存储器中的 列主 矩阵时 列被一个接一个地指定 使得存储器中的前 4 个条目对应于矩阵中的第一列 另一方面 行主 矩阵被理解为依次指定行 以便内存中的前 4 个条目指定矩阵的第一行 A GLKMatrix4看起来像这样 u
  • UICollectionView setLayout:animated: 不保留 zIndex

    我注意到打电话时setLayout animated in a UICollectionView要在两个布局之间切换 当前可见的单元格不遵循zIndex它的布局属性已设置在layoutAttributesForItemAtIndexPath
  • iOS:Facebook 登录访问令牌错误:由于模拟器错误,回退到从 NSUserDefaults 加载访问令牌

    根据说明进行配置后 我不断收到此错误 并且无法在我的应用程序上成功使用 Facebook 登录 我在 XCode 8 1 上运行它并使用 iOS 10 1 模拟器 我按照 Facebook iOS SDK 指南中的步骤操作 并将 Faceb
  • Mac 上的 Delphi - 可能吗? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我负责一个 Delphi Win32 项目管理应用程序 我刚刚完成了向 Delphi 2009 的迁移

随机推荐

  • JavaFx-14 resizeColumnToFitContent 方法

    JavaFx 14 将此方法放在 TableColumnHeader 中 而不是放在 Skin 中 如何从 TableColumn 和 TableView 中找到 TableColumnHeader 不知道你是否还需要这个 但如果其他人感兴
  • 使用高斯混合模型和 scikit learn 进行多类分类

    我正在尝试使用sklearn mixture GaussianMixture用于高光谱图像中的像素分类 有 15 个班级 1 15 我尝试使用该方法http scikit learn org stable auto examples mix
  • 如何为使用“create-react-app CLI”创建的现有React应用程序生成bundle.js

    这个问题与this https stackoverflow com questions 46564182 how to convert a react app to phonegap app所以帖子 我在用着create react app
  • BindingExpression 的源发生更改时发出通知?

    测试场景 我有一个带有 DependencyProperty A 的控件 我有一个带有属性 A 的 ViewModel 我使用 OneWayToSource 绑定将其绑定到控件的 A 属性 绑定由控件显式更新 我在运行时切换了绑定的 Vie
  • UICollectionView 的每个部分独立滚动?

    是否可以使用UICollectionView构建一个布局 其中每个section可以独立滚动吗 例如 假设有 20 行图像 其中每行都可以独立水平滚动以在屏幕外显示更多图像 在此过程中无需滚动其他行 并且整个视图可以垂直滚动以显示更多行 我
  • 使用until 参数调用Facebook News feed (me/home),每次连续调用返回limit/2

    这是我的代码的精简版本 没有初始化调用 一切都很完美 除了我每次连续调用都会传递一个until值转化为FB api 返回极限 2 我曾尝试使用不同的 Facebook 帐户在两次通话之间等待长达 1 分钟 但这对解决问题没有帮助 我检查了n
  • 如何从c++运行c++代码?

    如果我有一些 C 代码作为 C 程序中的字符串量 数据 我可以执行该字符串的内容吗 如在 C 中使用 CodeDOM 或 perl python 等中存在的 eval 函数 简短回答 你不能 稍微长一点的答案 c 没有反射 而且一般都是编译
  • Sublime Text 构建系统与选项

    我有一个项目的各种 shell 构建脚本 并且希望创建一个集中式构建系统 其中包含允许运行哪个 shell 脚本的选项 例如 用户按 Cmd B 然后用户会看到以下选项 1 shellscript1 sh2 shellscript2 sh3
  • 创建 XML 的最快且最有效的方法

    在 Java 中创建 XML 文档最快 最有效的方法是什么 那里有大量的库 woodstox xom xstream 只是想知道是否有人有任何输入 我应该采用代码生成方法 因为 xml 模式众所周知 吗 或者运行时的反射方法 编辑附加信息
  • 是否可以分层标记 matplotlib (pyplot) 条形图?

    我已经成功使用 matplotlib pyplot 绘制了以下条形图 该图来自聚合的 PANDAS DataFrame 如下所示 请注意 条形图中的每个条对应于mean柱子 另请注意 这些值是not零 但 PANDAS 输出0 and 0当
  • Python 3.6+ 格式化解包字典中缺少键的字符串

    在Python3 4中你可以做以下事情 class MyDict dict def missing self key return s key 然后是这样的 d MyDict d first name Richard print I am
  • Castle ActiveRecord 表名称冲突

    当你在 NHibernate 中遇到像 User 这样的保留字时 你只需在有问题的文本周围加上单引号 nHibernate 就会用方括号将文本括起来以进行查询 我的问题是如何使用 Castle ActiveRecord 做同样的事情 实际上
  • 发送包含自定义数据的 SNMP 陷阱

    客户要求我们将 SNMP 陷阱发送到他们的 Nagios 服务器 而不是电子邮件警报 昨天之前我对 SNMP 唯一的了解是它听起来像一个缩写词 所以请原谅 并纠正我 我对它可能有的任何误解 陷阱中需要发送的唯一信息与我们向客户端发出警报的事
  • 在 EC2 实例上启用 OpenSSL

    我目前正在尝试在我的 ec2 服务器上安装 openssl 以便我可以摆脱这个特定的错误 stream socket client SSL operation failed with code 1 OpenSSL Error message
  • 从第一个重定向到 HTTPS 的请求中删除 Azure Web App 上的响应服务器标头

    我正在尝试从 Azure Web 应用程序 使用 ASP Net core 应用程序 删除响应服务器标头 经过多次尝试改变网络配置并使用中间件删除应用程序代码中的标头 微软不会放弃并将响应标头设置为服务器 Microsoft IIS 10
  • 如何在 sqlproj (SQL Server 2012) 脚本中使用 msbuild 属性

    我刚刚将现有的 SQL Server 2008 r2 dbproj 升级到 SQL Server 2012 sqlproj 使用 SQL Server Data Tools 以前 我能够在项目中定义 SQLCMD 变量 然后通过添加以下元素
  • 查找列表元素的所有组合,包括重复元素

    我正在 python 中寻找一种算法 该算法将返回数字列表的所有可能组合 允许重复元素并加起来达到某个数字 例如 给定目标数字 7 以及列表 2 3 4 我希望能够生成以下组合 2 2 3 2 3 2 3 2 2 3 4 4 3 我了解如何
  • Ruby/Rails - 如何将秒转换为时间?

    我需要执行以下转换 0 gt 12 00AM 1800 gt 12 30AM 3600 gt 01 00AM 82800 gt 11 00PM 84600 gt 11 30PM 我想出了这个 0 84600 step 1800 n puts
  • lwuit.html.CSSEngine.applyStyleToUIElement 的 NullPointerException(来源未知)

    我正在执行以下 LinkedIn 登录过程 但不幸的是 在加载 LinkedIn 登录窗口时 授予 Yokeapp 访问您的 LinkedIn 帐户的权限 它没有显示任何内容并引发错误 我在 Eclipse pulsar 中使用 LWuit
  • 如何在 SwiftUI 中使视图的大小与另一个视图的大小相同

    我正在尝试重新创建 Twitter iOS 应用程序的一部分来学习 SwiftUI 并且想知道如何动态地将一个视图的宽度更改为另一个视图的宽度 就我而言 使下划线与文本视图的宽度相同 我附上了一张屏幕截图 以尝试更好地解释我所指的内容 任何