This gist shows how to add a tooltip including a button to show it in a header of a table created using SwiftUI. It is used in ASO Suite.
Last active
May 13, 2023 01:56
-
-
Save nielsmouthaan/1d30fb9ea7357c5ae8f6324747fc481e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
extension AnyHashable { | |
struct tooltip { | |
static let tag = "tooltip.tag" | |
static let point = "tooltip.point" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
extension Notification.Name { | |
static let tooltipButtonClicked = Notification.Name("tooltipButtonClicked") | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import Introspect // https://github.com/siteline/SwiftUI-Introspect | |
struct Item: Identifiable { | |
var id = UUID() | |
var name: String | |
} | |
struct TableWithTooltips: View { | |
@State var items: [Item] = [ | |
Item(name: "Some item"), | |
Item(name: "Another item"), | |
Item(name: "Yet another item") | |
] | |
@State private var tooltipVisibleForColumn: Int? | |
@State private var tooltipAnchorPoint = NSZeroPoint | |
var body: some View { | |
Table(items) { | |
TableColumn("Name") { | |
Text($0.name) | |
} | |
} | |
.introspectTableView { tableView in | |
let column = 0 | |
let title = tableView.tableColumns[column].title | |
tableView.tableColumns[column].headerCell = TooltipHeaderCell(textCell: title, tag:column) | |
} | |
.popover(isPresented: showingTooltip, attachmentAnchor: .rect(.rect(CGRect(x: tooltipAnchorPoint.x, y: tooltipAnchorPoint.y, width: 0, height: 0))), arrowEdge: .bottom, content: { | |
VStack { | |
Text("Hello!") | |
} | |
.padding() | |
.frame(width: 250) | |
}) | |
.onReceive(NotificationCenter.default.publisher(for: .tooltipButtonClicked)) { notification in | |
if let userInfo = notification.userInfo { | |
if let tag = userInfo[.tooltip.tag] as? Int, let point = userInfo[.tooltip.point] as? NSPoint { | |
tooltipVisibleForColumn = tag | |
tooltipAnchorPoint = point | |
} | |
} | |
} | |
} | |
private var showingTooltip: Binding<Bool> { | |
return Binding( | |
get: { tooltipVisibleForColumn != nil }, | |
set: { if !$0 { tooltipVisibleForColumn = nil } } | |
) | |
} | |
} | |
struct TestTable_Previews: PreviewProvider { | |
static var previews: some View { | |
TableWithTooltips() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import AppKit | |
class TooltipHeaderCell: NSTableHeaderCell { | |
init(textCell: String, tag: Int) { | |
super.init(textCell: textCell) | |
self.tag = tag | |
} | |
required init(coder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) | |
{ | |
super.drawInterior(withFrame: cellFrame, in: controlView) | |
for subview in controlView.subviews { | |
if subview.tag == tag { | |
subview.removeFromSuperview() | |
} | |
} | |
if cellFrame.origin.x == 0 { | |
return | |
} | |
let attributes: [NSAttributedString.Key: Any] = (font != nil) ? [.font: font!] : [:] | |
let titleWidth = (title as NSString).size(withAttributes: attributes).width | |
let x = titleRect(forBounds: cellFrame).origin.x + titleWidth + 15 | |
let frame = NSRect(x: x, y: titleRect(forBounds: cellFrame).origin.y, width: cellFrame.height, height: cellFrame.height) | |
let button = NSButton(frame: frame) | |
button.tag = tag | |
button.image = NSImage(systemSymbolName: "info.circle.fill", accessibilityDescription: nil) | |
button.imagePosition = .imageOnly | |
button.isBordered = false | |
button.target = self | |
button.action = #selector(buttonClicked(_:)) | |
controlView.addSubview(button) | |
} | |
@objc func buttonClicked(_ sender: NSButton) { | |
let point = NSPoint(x: sender.frame.midX, y: sender.frame.maxY) | |
NotificationCenter.default.post(name: .tooltipButtonClicked, object: self, userInfo: [ | |
.tooltip.tag: tag, | |
.tooltip.point: point | |
]) // Click handler is prefered, but that caused a crash so using a notification instead. | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment