如何获取 SwiftUI Text 中每个字符的位置

2023-12-06

我的第一个想法是基于文本+运算符。似乎很容易,通过组合/一个字符/一个字符/来构建整个文本并检查部分结果的宽度......不幸的是,我没有找到如何做到这一点的方法。所有已知的获取几何图形的技巧(alignmentGuide、GeometryReader、anchorPreferences ...)都可以用作视图修改器!这意味着 Text + 运算符无法使用。例如,简单地计算文本中字符的位置作为 Text(String(Character)) 宽度的总和是行不通的

Text("WAW")

and

HStack(spacing:0) { Text("W"); Text("A"); Text("W") }

宽度(如预期)不同。

最后我得到了(使用复制粘贴来检查它)类似的东西

struct ContentView: View {
    @State var width: [CGFloat] = []
    let font = Font.system(size: 100)
    var body: some View {
        VStack {
            if width.isEmpty {
                text(t: Text("W").font(font), width: $width)
                text(t: Text("WA").font(font), width: $width)
                text(t: Text("WAW").font(font), width: $width)
                text(t: Text("WAWE").font(font), width: $width)
            } else {
                ZStack(alignment: .topLeading) {
                    Text("WAWE").font(font).border(Color.red)
                    Path { path in
                        path.move(to: CGPoint(x: 0, y: 0))
                        path.addLine(to: CGPoint(x: 0, y: 150))
                    }.stroke(lineWidth: 1)
                    Text("\(0)").rotationEffect(Angle(degrees: 90), anchor: .bottom)
                        .position(CGPoint(x: 0, y: 170))

                    ForEach(Array(width.sorted(by: <).enumerated()), id: \.0) { p in
                        ZStack {
                            Path { path in
                                path.move(to: CGPoint(x: p.1, y: 0))
                                path.addLine(to: CGPoint(x: p.1, y: 150))
                            }.stroke(lineWidth: 1)

                            Text("\(p.1)").rotationEffect(Angle(degrees: 90), anchor: .bottom).position(CGPoint(x: p.1, y: 170))
                        }
                    }
                }.padding()
            }
        }
    }
}
func text(t: Text, width: Binding<[CGFloat]>)->some View {
    let tt = t.background(
        GeometryReader{ proxy->Color in
            DispatchQueue.main.async {
                width.wrappedValue.append(proxy.size.width)
            }
            return Color.clear
        }
    )
    return tt.background(Color.yellow)
}

有了这个结果

enter image description here

Which 有效,但非常黑客解决方案

我正在寻找更好的方法!

UPDATE with center of each character enter image description here


这种方法行不通。字符串的文本布局与单个字符的布局有很大不同。您在本文中要解决的问题是字距调整,但您仍然需要处理连字、组合字符和字母形式(尤其是阿拉伯语)。文本在这里是错误的工具。

在 SwiftUI 中你确实无法做到这一点。您需要使用CoreText(CTLine)或TextKit(NSLayoutManager)。

也就是说,这并不能保证与文本完全匹配。我们不知道 Text 会做什么。例如,当出现比预期更小的框架时,它会收紧间距吗?我们不知道,也不能问(如果知道的话,这种方法也无法处理)。但 CoreText 和 TextKit 至少会给您可靠的答案,您可以使用它们自己布局与您生成的指标相匹配的文本。


虽然我不认为这种方法是您想要的方式,但代码本身可以改进。首先,我建议优先选择在 GeometryReader 内部调用 async。

struct WidthKey: PreferenceKey {
    static var defaultValue: [CGFloat] = []
    static func reduce(value: inout [CGFloat], nextValue: () -> [CGFloat]) {
        value.append(contentsOf: nextValue())
    }
}

您可以使用以下命令将宽度数据捕获到其中:

extension View {
    func captureWidth() -> some View {
        background(GeometryReader{ g in
            Color.clear.preference(key: WidthKey.self, value: [g.size.width])
        })
    }
}

稍后将阅读此内容onPreferenceChange:

    .onPreferenceChange(WidthKey.self) { self.widths = $0 }

作为字符串的助手:

extension String {
    func runs() -> [String] {
        indices.map { String(prefix(through: $0)) }
    }
}

有了这些,我们可以编写一个 captureWidths() 函数来捕获所有宽度,但隐藏结果:

func captureWidths(_ string: String) -> some View {
    Group {
        ForEach(string.runs(), id: \.self) { s in
            Text(verbatim: s).captureWidth()
        }
    }.hidden()
}

请注意,字体尚未设置。这是故意的,它会被称为这样:

        captureWidths(string).font(font)

这适用.font到组,该组将其应用于其中的所有文本。

还要注意使用verbatim此处(以及稍后创建最终文本时)。默认情况下,传递给 Text 的字符串不是文字。它们是本地化键。这意味着您需要查找正确的本地化值来分解字符。这增加了一些复杂性,我假设你不想要,所以你应该明确地说这个字符串是逐字的(字面意思)。

以及所有一起:

struct WidthKey: PreferenceKey {
    static var defaultValue: [CGFloat] = []
    static func reduce(value: inout [CGFloat], nextValue: () -> [CGFloat]) {
        value.append(contentsOf: nextValue())
    }
}

extension View {
    func captureWidth() -> some View {
        background(GeometryReader{ g in
            Color.clear.preference(key: WidthKey.self, value: [g.size.width])
        })
    }
}

extension String {
    func runs() -> [String] {
        indices.map { String(prefix(through: $0)) }
    }
}

func captureWidths(_ string: String) -> some View {
    Group {
        ForEach(string.runs(), id: \.self) { s in
            Text(s).captureWidth()
        }
    }.hidden()
}

struct ContentView: View {
    @State var widths: [CGFloat] = []
    @State var string: String = "WAWE"

    let font = Font.system(size: 100)
    var body: some View {
        ZStack(alignment: .topLeading) {
            captureWidths(string).font(font)

            Text(verbatim: string).font(font).border(Color.red)
            Path { path in
                path.move(to: CGPoint(x: 0, y: 0))
                path.addLine(to: CGPoint(x: 0, y: 150))
            }.stroke(lineWidth: 1)
            Text("\(0)").rotationEffect(Angle(degrees: 90), anchor: .bottom)
                .position(CGPoint(x: 0, y: 170))

            ForEach(widths, id: \.self) { p in
                ZStack {
                    Path { path in
                        path.move(to: CGPoint(x: p, y: 0))
                        path.addLine(to: CGPoint(x: p, y: 150))
                    }.stroke(lineWidth: 1)
                    Text("\(p)").rotationEffect(Angle(degrees: 90), anchor: .bottom).position(CGPoint(x: p, y: 170))
                }
            }
        }
        .padding()
        .onPreferenceChange(WidthKey.self) { self.widths = $0 }
    }
}

不过,要了解该算法对于并不简单的事情的行为方式:

enter image description here

在从右到左的文本中,这些划分是完全错误的。

enter image description here

注意 T 框太窄了。这是因为在 Zapfino 中,Th 连字比字母 T 加字母 h 宽得多。 (公平地说,Text 几乎无法处理 Zapfino;它几乎总是会剪辑它。但关键是连字可以显着改变布局,并且存在于许多字体中。)

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

如何获取 SwiftUI Text 中每个字符的位置 的相关文章

  • 具有多个 ViewBuilder 的 SwiftUI 视图

    我有一个视图 表示单元格中的一行 如下所示 这很有效 但三个水平元素 图像 标题 副标题 图像 被硬编码为其各自的类型 我想要一个通用的ThreeItemView这可能需要 3Views任何类型并如图所示排列它们 这将允许我将相同的容器布局
  • 使用 Swift(使用 SwiftJWT)和 REST API 连接到 Apple Store Connect - 失败并出现 401

    我正在尝试通过他们的 REST API 连接到 Apple Store Connect 虽然这在几天前有效 但我无法弄清楚为什么它停止工作 现在我无法通过身份验证 即我发出的服务器响应的每个请求都是 401 我是否遗漏了什么 我做什么 生成
  • 如何使用 SwiftUI 创建自定义滑块?

    我可以使用 SwiftUI 创建滑块 但无法更改滑块的样式 如下图所示 问题 我无法在 SwiftUI 中找到任何选项来更改滑块样式 注意 我想仅使用 SwiftUI 创建它 我已经在 Swift 中使用 创建了这个滑块 https git
  • 如何将 WKUIDelegate 实现到 SwiftUI WKWebView 中?

    我正在 Xcode v11 上创建一个 Web 应用程序 但在实现 WKUIDelegate 来显示 Javascript 警报并在 Web 应用程序上正确确认时遇到问题 我在 ContentView swift 上得到了一个非常简单的 w
  • 如何在 SwiftUI 中将变量从一个视图传递到另一个视图

    我正在尝试将一个变量从一个视图传递到 SwiftUI 中的另一个视图 我有一个重置按钮 我想在另一个视图中将变量设置为零 我尝试在视图一中创建一个新结构并在视图2中访问该变量 View 1 State var count MyNumber
  • 如何将一个 SwiftUI View 作为变量传递给另一个 View 结构

    我正在实施一个very自定义 NavigationLink 称为MenuItem并希望在整个项目中重用它 它是一个符合以下条件的结构体View并实施var body some View其中包含一个NavigationLink 我需要以某种方
  • 在 UIViewRepresentable CollectionView(包装的 UICollectionView)中使用 UICollectionViewCell 的 SwiftUI 视图

    我必须更换现有的SwiftUI List的视图UICollectionView 因为应用程序设计已更新 并且新设计对于 SwiftUI 来说非常复杂 因此必须作为自定义实现UICollectionViewFlowLayout 所以视图 现在
  • SwiftUI - SwiftUI 中有等效的 popViewController 吗?

    我正在使用 SwiftUI 希望在点击按钮时能够返回到之前的视图 与我们使用的相同popViewController里面一个UINavigationController 到目前为止有提供的方法吗 我也尝试过使用NavigationDesti
  • 如何使用 SwiftUI 拖动工作滑块

    我想拖动一个滑块 当然也让它滑动 我可以做其中之一 但我不能两者都做 如何拖动并拥有可用的滑块 我也尝试找到一种方法来删除手势 但我找不到方法来做到这一点 还尝试了 Apple Composition SwiftUI Gestures 文档
  • 我想为 TabBar 设置背景

    I don t know how to change the background of the TabBar by an Image I try to change the background of TabBar not try to
  • 如何在 SwiftUI 中同时检测链接上的点击和点击坐标?

    在我的 SwiftUI 应用程序中 文本的某些部分需要可点击 点击时 应该发生一些自定义操作 不一定要打开网页 同时我需要检测点击坐标 我打算为此使用拖动手势处理程序 我使用以下方法实现了可点击文本作为链接AttributedString
  • 在 SwiftUI 文档应用程序中,如何从函数内保存文档

    当前版本的 Xcode 版本 12 5 1 为 macOS 的基于文档的应用程序提供了一个模板 提供以下文档模型 struct MyDocument FileDocument var text String init text String
  • ScrollView 内的 GeometryReader 会折叠 GeometryReader,无论子尺寸如何

    我正在尝试创建一个包含绘图的子视图 我希望该绘图的大小与父容器的一部分成比例 除非将其放入层次结构上方的滚动视图中 否则效果很好 从屏幕截图中可以看出 堆栈内部的声音正如预期的那样最大 但是 当您放入滚动视图时 几何读取器的大小会折叠到接近
  • SwiftUI NavigationView 从其内部开始

    因此 我在页面视图样式的 TabView 中嵌入了一个 NavigationView 首次加载时 NavigationView 将在其内部启动 然后重新加载后即可正常显示 我不确定是什么原因造成的 我制作了一个 GIF 来更好地说明问题 这
  • WidgetKit 并发症不会更新

    我们正在迁移ClockKit并发症WidgetKit在我们的手表应用程序中 watchOS 9 迁移很顺利 UI部分工作得很好 然而 我们遇到了小部件在手表应用程序请求时未更新的问题 我相信我们错过了一些非常简单和基本的东西 但到目前为止还
  • SwiftUI - NavigationSplitView 内的 NavigationStack 内的嵌套链接不起作用

    我正在尝试 ipadOS16 macOS13 中提供的新导航 API 但在弄清楚如何在 macOS 13 上将 NavigationSplitView NavigationStack 和 NavigationLink 组合在一起时遇到了一些
  • 使 Picker 与其他 BinaryInteger 类型兼容

    Picker仅当与以下一起使用时才能正常工作Int 当使用任何其他类型的BinaryInteger它根本不更新选择 为了解决这个问题 我想做一个CompatilibityPicker但我必须承认我的理解Binding实际上工作给我带来了很多
  • Xcode 12 根本没有调用动态链接 Firebase 函数?

    因此 我有一个正在运行的动态链接 当我单击它时它会打开应用程序 但不会发生动态链接的处理 这是因为下面看到的应用程序功能从未输入过 我不知道为什么 func handleIncomingDynamicLink dynamicLink Dyn
  • SwiftUI 动画滑入和滑出

    我有一个视图将显示在另一个视图上 视图动画完美地从右侧滑入 但是当我单击 关闭 按钮时 视图消失 而没有在消失之前滑回右侧的所需动画 我尝试过使用 opacity self isShowing 1 0 但随后视图淡入和淡出 我需要它滑入和滑
  • SwiftUI:发送电子邮件

    在正常情况下UIViewController在 Swift 中 我使用此代码发送邮件 let mailComposeViewController configuredMailComposeViewController mailCompose

随机推荐

  • 如何使用服务器端加密将spark rdd写入S3

    我正在尝试使用服务器端加密将 RDD 写入 S3 中 以下是我的一段代码 val sparkConf new SparkConf setMaster local setAppName aws encryption val sc new Sp
  • Gulp - 按文件夹编译sass,并修改父目录

    我是 gulpfile 的新手 我不知道如何使用单个任务迭代多个文件夹 我的 src 文件夹结构 folder1 assets style scss folder2 assets style scss folder3 subfolder1
  • asp mvc 列出具有动态变化规格的电子商务产品

    我正在尝试为电子商务网站开发产品详细信息页面 假设我们在视图中列出一些产品 现在当用户单击产品时 他会进入产品详细信息视图以显示产品规格 包括添加到购物车 评论等 但这里是产品每个产品的规格都会动态变化 example 服装产品 Size
  • 使用python读取middlebury'flow'文件(字节数组和numpy)

    我正在尝试将 flo 文件读取为 numpy 2Channels 图像 格式描述如下 flo file format used for optical flow evaluation Stores 2 band float image fo
  • 为什么 java 中的操作赋值运算符类型不安全?

    我不确定这个问题是否表述清楚 但举个例子会更清楚 我发现这在 Java 中不起作用 int a a 5 0 但这将 int a a 5 0 即 看起来 运算符是类型安全的 但 不是 这是否有任何深层原因 或者这只是语言设计者必须做出的另一个
  • libGdx 如何使用图像或演员作为主体

    我浏览了 libGdx wiki 教程 但没有找到使用图像或演员作为物理体的示例 在我的游戏中 我在舞台上添加了一名演员 但我想添加这个演员或精灵图像作为物理体 我必须拖动这个演员 甚至想要检测与其他物体的碰撞 如果有请给我参考 Thank
  • 修改shell脚本来监控/ping多个ip地址

    好吧 所以我需要不断监控多个路由器和计算机 以确保它们保持在线 我找到了一个很棒的剧本here如果无法 ping 通单个 IP 它将通过咆哮通知我 这样我就可以在手机上收到即时通知 我一直在尝试修改脚本以 ping 多个地址 但运气不佳 当
  • 执行从 Xib 到 ViewController 的 segue

    我有一个带有按钮的 Xib 文件 单击按钮时我想转到另一个视图控制器 我已经在 StoryBoard 中的视图控制器之间创建了一个 Segue 并创建了一个标识符 但似乎无法以编程方式调用它 IBAction func buttonActi
  • Log4j |更新Appender的日志级别

    我的 log4j properties 文件 log4j rootLogger INFO stdout console output appender log4j appender stdout org apache log4j Conso
  • pandas“DataFrame”对象没有属性“map”

    我有两个 df df a 和 df b df a number cur code 1000 USD 700 2000 USD 800 3000 USD 900 df b number amount deletion code 1000 0
  • 在链接器方法之外将元素的文本存储在 Cypress 中

    如何存储 div 的文本值一次并在整个 cypress 测试中使用它 到目前为止 我已经通过将大部分测试逻辑嵌套在调用中来做到这一点then方法 但这看起来并不优雅或理想 cy get div then div gt let storedV
  • 如何将文件缓冲区转换为 标签 src?

    我正在开发一个应用程序 使用 Node js 作为后端 并作为我的前端进行反应 现在我创建了一个上传文件并将其作为缓冲区类型存储在 mongodb 中的路由 我的问题是 当我在 React 应用程序中收到这些数据时 如何使用这些数据将其转换
  • AVPlayer 无法从本地文件播放 m3u8

    我正在尝试让 AVPlayer 播放本地文件的 m3u8 播放列表 我使用 Apple 的示例播放列表之一将其范围缩小为一个简单的测试用例 https tungsten aaplimg com VOD bipbop adv fmp4 exa
  • 创建实体框架模型时忽略数据库默认值

    假设我的数据库中有下表 CREATE TABLE dbo Test Id INT IDENTITY 1 1 NOT NULL Active BIT DEFAULT 1 NOT NULL When creating an EF model f
  • 学习汇编-echo程序名

    我正在尝试用汇编语言编写一个简单的程序来写出程序的名称 使用 gdb 进行调试 我确定对 sys write 的调用返回 14 EFAULT 我还能够验证我的 strlen 函数是否正常工作 似乎存在某种内存访问问题 但考虑到 strlen
  • 如何让我的 kubernetes 获取最新的 docker 镜像? [复制]

    这个问题在这里已经有答案了 Problem Slack 和 Stack Overflow 上出现的一个常见问题是 当镜像标签未更改但底层镜像已更改时 如何触发 Deployment RS RC 的更新 考虑 存在一个带有映像 foo lat
  • 如何将发件人地址设置为其他gmail中的任何电子邮件(通过Gmail在.NET中发送电子邮件)?

    在这篇文章中通过 Gmail 在 NET 中发送电子邮件我们有一个通过gmail发送电子邮件的代码 在发送邮件中我们从字段中找到包含我使用的gmail帐户我使用相同的代码 但通过将发件人地址更改为我想要的任何电子邮件 并在凭据中设置 gma
  • Spark 2.4 的 lineSep 选项

    Is lineSep该选项确实适用于 Spark 2 4 版本 lineSep default covers all r r n and n defines the line separator that should be used fo
  • 如何修复 - 41:无法从静态上下文引用非静态变量 -> 这是什么原因?

    我正在尝试编写此代码来获取第一个initialCapacity素数 然后使用java按顺序打印它们 它不起作用有两个原因 首先我收到错误 41 不能从静态上下文引用非静态变量 listOfPrimeNumbers 当我尝试运行该程序时 但即
  • 如何获取 SwiftUI Text 中每个字符的位置

    我的第一个想法是基于文本 运算符 似乎很容易 通过组合 一个字符 一个字符 来构建整个文本并检查部分结果的宽度 不幸的是 我没有找到如何做到这一点的方法 所有已知的获取几何图形的技巧 alignmentGuide GeometryReade