SwiftUI 2.0
就像它的名字一样简单
Button("Action") { }
.help("Just do something")
Button("Action") { }
.help(Text("Just do something"))
在SwiftUI 2中:
Toggle("...", isOn: $isOn)
.help("this is tooltip")
在SwiftUI 1中,实际上没有本地方法来创建工具提示。但是这里也有一个解决方案:
import SwiftUI
@available(OSX 10.15, *)
public extension View {
func tooltip(_ toolTip: String) -> some View {
self.overlay(TooltipView(toolTip))
}
}
@available(OSX 10.15, *)
private struct TooltipView: NSViewRepresentable {
let toolTip: String
init(_ toolTip: String) {
self.toolTip = toolTip
}
func makeNSView(context: NSViewRepresentableContext<TooltipView>) -> NSView {
NSView()
}
func updateNSView(_ nsView: NSView, context: NSViewRepresentableContext<TooltipView>) {
nsView.toolTip = self.toolTip
}
}
使用方法:
Text("some text with tooltip")
.tooltip("some tooltip")
TooltipView
中初始化 toolTip
为可选类型,有什么原因吗? - soundflix@available(OSX 11.0, *)
不是必需的,因为它也适用于10.15,在SwiftUI之前也无法使用,所以我们可以完全省略它。此外,主要好处是它确实在10.15中起作用,否则我们可以使用.help(..)
。import Foundation
也不是必需的,因为它将随SwiftUI一起导入。 - soundflixextension View {
func tooltip(_ tip: String) -> some View {
background(GeometryReader { childGeometry in
TooltipView(tip, geometry: childGeometry) {
self
}
})
}
}
private struct TooltipView<Content>: View where Content: View {
let content: () -> Content
let tip: String
let geometry: GeometryProxy
init(_ tip: String, geometry: GeometryProxy, @ViewBuilder content: @escaping () -> Content) {
self.content = content
self.tip = tip
self.geometry = geometry
}
var body: some View {
Tooltip(tip, content: content)
.frame(width: geometry.size.width, height: geometry.size.height)
}
}
private struct Tooltip<Content: View>: NSViewRepresentable {
typealias NSViewType = NSHostingView<Content>
init(_ text: String?, @ViewBuilder content: () -> Content) {
self.text = text
self.content = content()
}
let text: String?
let content: Content
func makeNSView(context _: Context) -> NSHostingView<Content> {
NSViewType(rootView: content)
}
func updateNSView(_ nsView: NSHostingView<Content>, context _: Context) {
nsView.rootView = content
nsView.toolTip = text
}
}
GeometryReader
添加到工具提示的内容中,然后将工具提示的大小限制为与内容的大小相匹配。Toggle("...", isOn: $isOn)
.tooltip("This is my tip")
最新版本的SwiftUI在tooltip
方法上需要进行一些小的更改。我注意到存在快速重绘问题。通过添加ZStack
,可以解决这个问题:
extension View {
func tooltip(_ tip: String) -> some View {
ZStack {
background(GeometryReader { childGeometry in
TooltipView(tip, geometry: childGeometry) {
self
}
})
// self
}
}
}
2023年2月:我移除了第二个自我。有了多余的自我,父母会发出光芒。
当覆盖层不够好用时,例如您想要在接受鼠标事件的控件(而覆盖层将不允许通过点击),如Toggle上使用工具提示,则解决方案可能是使用一个包含NSHostingView本身的主机视图 - 它支持AppKit视图作为工具提示 - 最终在内部加载更多SwiftUI内容:
struct Tooltip<Content: View>: NSViewRepresentable {
typealias NSViewType = NSHostingView<Content>
init(_ text: String?, @ViewBuilder content: () -> Content) {
self.text = text
self.content = content()
}
let text: String?
let content: Content
func makeNSView(context: NSViewRepresentableContext<Tooltip<Content>>) -> NSViewType {
NSViewType(rootView: content)
}
func updateNSView(_ nsView: NSViewType, context: NSViewRepresentableContext<Tooltip<Content>>) {
nsView.rootView = content
nsView.toolTip = text
}
}
使用这个方法时,如果与某些SwiftUI内容一起使用,则需要注意大小问题(然后您可以使用fixedSize()或frame(width:height:)使其按照您的需求工作),但除此之外它非常易于使用:
Tooltip("A description") {
Toggle("...", isOn: $isOn)
}
struct TooltipText: View {
@State private var isActive = false
let text: String
let helpText: String
var body: some View {
Text(isActive ? helpText : text)
.padding( isActive ? 6 : 0)
.background(Color.white)
.overlay(
RoundedRectangle(cornerRadius: 3)
.stroke(Color.blue, lineWidth: isActive ? 1 : 0)
)
.animation(.easeOut(duration: 0.2) )
.gesture(DragGesture(minimumDistance: 0)
.onChanged( { _ in isActive = true } )
.onEnded( { _ in isActive = false } )
)
}
}
使用:
TooltipText(text: "sum of squares:", helpText: "sum of data (with mean subtracted) squared")
你也可以在界面构建器(在Xcode中以图形方式布局您的视图)中完成此操作。当您添加按钮时,“Identity Inspector”选项卡 - 位于屏幕右侧面板上默认的“Attributes Inspector”选项卡之前...还有一个“Tooltip”字段。
acceptsFirstMouse
的问题时所描述的相同。 - Asperi