How do view types like Text or Image conform to the View protocol in SwiftUI?
This question was asked on June 6, 2019, during WWDC, when we only had the first beta of Xcode 11 and SwiftUI. So answering this question correctly requires access to that version of SwiftUI. You can download Xcode 11 beta 1 here. (Thank you, xcodereleases.com!) You are in for an adventure trying to unpack the archive, though, because (I think) it was signed with a certificate that has since expired. I resorted to black magic (stepping through the xip
command in LLDB and modifying memory at a key moment to subvert the certificate verification). You could perhaps just set your system time back to June 6, 2019 before unpacking.
Anyway, here's the secret to understanding why Text
didn't appear to conform to View
: Xcode, and Apple's documentation generator, intentionally omit identifiers in the SDK that start with _
.
So if you want to see the full public declaration of a type, you cannot rely on Xcode or the documentation to show it to you. Instead, you have to dig up the .swiftinterface
file for the module. For SwiftUI, you can find it here, relative to the Xcode.app
directory:
Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/SwiftUI.framework/Modules/SwiftUI.swiftmodule/arm64.swiftinterface
In the Xcode 11 beta 1 version of that file, you won't find a direct conformance Text: View
. Instead, you will find this:
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension Text : _UnaryView {
public static func _makeView(view: _GraphValue<Text>, inputs: _ViewInputs) -> _ViewOutputs
public typealias Body = Swift.Never
}
And you will find that _UnaryView
is a sub-protocol of View
:
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol _UnaryView : SwiftUI.View where Self.Body : SwiftUI._UnaryView {
}
So, in Xcode 11 beta 1 and the corresponding iOS, macOS, tvOS, and watchOS betas, Text
conforms to View
indirectly, through its conformance to _UnaryView
. Since _UnaryView
is part of the SDK and begins with _
, Xcode and the Apple documentation hide that symbol. So you can't see the conformance through normal methods.
At some later point (but still, I believe, during the Xcode 11.0 beta period), Apple eliminated the _UnaryView
protocol and made Text
conform directly to View
. So if you check the SwiftUI .swiftinterface
file in Xcode 11.4 (the current version as I write this), you'll find this:
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension Text : SwiftUI.View {
public static func _makeView(view: SwiftUI._GraphValue<SwiftUI.Text>, inputs: SwiftUI._ViewInputs) -> SwiftUI._ViewOutputs
public typealias Body = Swift.Never
}
How to fix Type 'ContentView' does not conform to protocol 'View' etc. XCODE - SwiftUI
It's because you didn't put closing brackets for scannerSheet
, and had extra closing brackets after the body variable.
This should work.
import SwiftUI
import CodeScanner
struct ContentView: View {
@State var isPresentingScanner = false
@State var scannedCode: String = "Scan a QR code to get started."
var scannerSheet : some View {
CodeScannerView(
codeTypes: [.qr],
completion:{ result in
if case let.success(code) = result {
self.scannedCode = code.string
self.isPresentingScanner = false
}
}
)
} // ←
var body: some View {
VStack(spacing: 10) {
Text(scannedCode)
Button("Scan QR code") {
self.isPresentingScanner = true
}
.sheet(isPresented: $isPresentingScanner) {
self.scannerSheet
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
P.S. Something that would help you spot this type of error in the future would be to re-indent all the code.
You can do that by selecting all the code in the file (CMD
+A
), then selecting Editor -> Structure -> Re-Indent (CTRL
+I
)
SwiftUI: ViewModifier where content is an Image
In this case where the modification is specific to a particular view type, Image
say, you could just add an extension on that view type directly:
extension Image {
func myImageModifier() -> some View {
self
.resizable()
.aspectRatio(1.0, contentMode: .fit)
.clipShape(Circle())
}
}
A full playground text example follows. If you add a cute otter picture in your playground "Resources" folder named "Otter.png" you get a prettier result :)
import PlaygroundSupport
import SwiftUI
let image = (UIImage(named: "Otter.png") ?? UIImage(systemName: "exclamationmark.square")!)
struct ContentView: View {
var body: some View {
VStack {
Text("hello world")
Image(uiImage: image)
.myImageModifier()
}
}
}
extension Image {
func myImageModifier() -> some View {
self
.resizable()
.aspectRatio(1.0, contentMode: .fit)
.clipShape(Circle())
}
}
PlaygroundPage.current.liveView = UIHostingController(rootView: ContentView())
How to treat if-let-else as a single view in SwiftUI?
Make it closure argument a view builder, like
extension View {
func statusBar<V: View>(@ViewBuilder statusBar: () -> V) -> some View {
self.modifier(StatusBarView(statusBar: statusBar))
}
}
the same can be done in init of modifier, but not required specifically for this case of usage.
Tested with Xcode 13.4 / iOS 15.5
TextField ViewModifier not conforming to ViewModifier?
You likely have a struct
/class
in your project named Content
If you have Xcode's standard dark theme the "mint"/"greenish" means it is "Project" defined.
When you are using Apple's definition it is pinkish/purple like ViewModifier
, View
, and String
in your screenshot.
Search for struct Content
, class Content
, enum Content
, etc. In your project, You will find the duplicate and then just change the name of the duplicate.
It could also be a generic <Content: SomeProtocol>
or <Content>
or typealias Content
You can confirm the duplicate by being more specific
How to use any Object and pass it into a Generic SwiftUI View?
The var component: any Component
declaration means that the property can virtually hold any value that conforms to the protocol, meaning the exact type of the value stored there is to be determined at runtime.
On the other hand, ComponentView<C: Component>
is a compile-time construct, and the compiler needs to know which type will fill in the blanks, something that is not possible due to the any
construct.
You'll have to either
- propagate
any
downstream - fromRootView
toComponentView
, and remove the generic onComponentView
, or - propagate the generic requirement upstream - from
ComponentView
toRootView
, and makeRootView
generic.
Related Topics
Different Cell in Tableview Swift 3
In Collectionview How to Set Colors According to Selection
How to Read Heart Rate from iOS Healthkit App Using Swift
Differrence Between Closure and Function as Argument in Swift
iOS with Parse. Pfuser.Currentuser() Not Getting Cached. Returns Nil After App Restart
Return in Function Without Return Value in Swift
Saving Already Created Live Photos
Receipt Validation on iOS In-App-Purchase Returns Multiple Transaction
How to Call Presentviewcontroller in Uiview Class
Swift 2 Unused Constant Warning
It Is Possible to Know If a String Is Encoded in Base64
Restkit, Coredata and Swift - I Can't Seem to Fetch Results Back Out
How to Send Different Users to Separate View Controllers Using Firebase and Xcode
Timer Label Not Updated After Switching Views (Swift)