In SwiftUI there is this function and I can assign tag to View
func tag<V>(_ tag: V) -> some View where V : Hashable
Is there possibility to access this tag like so
MenuItem().tag(1)
and then in MenuItem I have some Button(action: ..., label: ...) and would like to access tag of Button
self.tag
I know I can just pass to MenuItem(tag: Int) like this but consider whether I can achieve something similar like in TabView where each tabItem has tag() assigned to it. How it uses it?
An answer of sorts: afaik you can't get the tag, but you can get an index from the local context. For example:
ForEach(0..<2, id: \.self) { buttonIndex in
Button(action: {
print("MY NUMBAH BE:", buttonIndex)
}) {
Text("WHAT MY NUMBAH?")
}
}
I saw this question scrolling through the 'Unanswered' questions here on SO, and so I thought I would try solve it!
I solved this with:
Mirror
for reflecting views to get a representation of the instance.withUnsafeBytes(_:)
to convert types we don't have access to.All the code is on GitHub at GeorgeElsham/TagExtractor, fully documented. You can easily add the Swift Package to your project. For keeping it short here, the documentation has been removed so this here is mostly just a rough overview of how it works.
Code:
extension View {
func getTag<TagType: Hashable>() throws -> TagType {
// Mirror this view
let mirror = Mirror(reflecting: self)
// Get tag modifier
guard let realTag = mirror.descendant("modifier", "value") else {
// Not found tag modifier here, this could be composite
// view. Check for modifier directly on the `body` if
// not a primitive view type.
guard Body.self != Never.self else {
throw TagError.notFound
}
return try body.getTag()
}
// Bind memory to extract tag's value
let fakeTag = try withUnsafeBytes(of: realTag) { ptr -> FakeTag<TagType> in
let binded = ptr.bindMemory(to: FakeTag<TagType>.self)
guard let mapped = binded.first else {
throw TagError.other
}
return mapped
}
// Return tag's value
return fakeTag.value
}
func extractTag<TagType: Hashable>(_ closure: (() throws -> TagType) -> Void) -> Self {
closure(getTag)
return self
}
}
enum TagError: Error, CustomStringConvertible {
case notFound
case other
public var description: String {
switch self {
case .notFound: return "Not found"
case .other: return "Other"
}
}
}
enum FakeTag<TagType: Hashable> {
case tagged(TagType)
var value: TagType {
switch self {
case let .tagged(value): return value
}
}
}
The documentation on the methods in the repo explain how to use this more in-depth, but here is an overview of 2 examples.
let view = Text("Hello world!").tag("some tag")
// Method #1
do {
let tag: String = try view.getTag()
print("tag: \(tag)")
} catch {
fatalError("Tag error: \(error)")
}
// Method #2
let tag: String? = try? view.getTag()
print("tag: \(tag)")
Text("Hello world!")
.tag("some tag")
.extractTag { (getTag: () throws -> String) in
// Method #1
do {
let tag = try getTag()
print("tag: \(tag)")
} catch {
print("Tag error: \(error)")
}
// Method #2
let tag = try? getTag()
print("tag: \(tag)")
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With