"Function declares an opaque return type [...]" error when declaring a view as a variable inside the body of a View in SwiftUI - swift

Assume I have a View with an Image that has a shadow property:
struct ContentView: View {
var body: some View {
Image("turtlerock").shadow(radius: 10)
}
}
Now imagine I want to access the value of the shadow radius. I assumed I could do this:
struct ContentView: View {
var body: some View {
let myImage = Image("turtlerock").shadow(radius: 10)
print(myImage.modifier.radius)
}
}
However, this returns an error:
Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
Is there a way to accomplish this somehow?

When modifying and building views, you can do this without a return statement and a building block one above the other without commas. This is called a multi-statement closure. When you try to create a variable inside a multi-statement closure, the compiler is going to complain because there is a mismatch in types (you can only combine views one after another, nothing more). See this answer for more details: https://stackoverflow.com/a/56435128/7715250
A way to fix this is to explicitly return the views you are combining, so you don't make use of the multi-closure statements:
struct MyView: View {
var body: some View {
let image = Image("Some image").shadow(radius: 10)
let myRadius = image.modifier.radius
// Do something with myRadius
return image // No multi closure statements.
}
}

If your view you want to reference is inside a stack, you should declare it outside the stack like this:
var body: some View {
let myImage = Image("image").shadow(radius: 10)
let stack = HStack {
myImage
Image("image2")
}
return stack
}

You can define the image outside body:
let myImage = Image("turtlerock").shadow(radius: 10)
var body: some View {
myImage
}
To print the radius you can do like so:
var body: some View {
myImage
.tapAction {
print(self.myImage.modifier.radius) // 10.0
}
}

When it happens to me in a testing environment I just nest everything in the body inside a
return ZStack{ ...}
A bit quick and dirty, but it works for my purposes.

I'm using Group {}:
func makeContentView() -> some View {
Group {
if some_condition_here {
Text("Hello World")
.foregroundColor(.red)
.font(.system(size: 13, weight: .bold, design: .monospaced))
} else {
Rectangle()
.fill(Color.gray20)
}
}
}
}

Related

How can I update modifier in SwiftUI without re drawing views

I want update my modifier in this way that the orignal content does not get redrawen or re appear for that goal I am using this code:
struct BigModified: ViewModifier {
func body(content: Content) -> some View {
return content
.font(Font.title.bold())
}
}
struct ContentView: View {
#State private var start: Bool = false
var body: some View {
Text("Hello, world!")
.modifier(start ? BigModified() : EmptyModifier())
.onTapGesture { start.toggle() }
}
}
The error is:
Result values in '? :' expression have mismatching types 'BigModified' and 'EmptyModifier'
I know that I can use switch or if, but again those make view get appear again. Is there a way around to update modifier with a State wrapper like in question, also I should add and tell it is obvious that I can use a parameter inside a ViewModifier for that! But that is not my question, my question is about .modifier(T) itself, more looking some thing like .modifier(T, V, U, ...) be possible to use or build, which takes more than just one viewModifier.
You can declare a view modifier in another way, as a func of View, so you can act on your view based on a boolean parameter.
This is how it works:
Modifier:
extension View {
#ViewBuilder
func modifier(big: Bool) -> some View {
if big {
self
.font(Font.title.bold())
} else {
self
}
}
}
How to use it:
var body: some View {
Text("Hello, world!")
.modifier(big: start)
.onTapGesture { start.toggle() }
}

Why I cannot use strokeBorder on content that conforms to Shape in SwiftUI?

I have a CustomShapeView which basically takes a Shape content, so after importing in to CustomShapeView I want use a modifier that works on Shape, but in my case it does not! I am wondering what I am missing in between?
My Goal: I want to be able to import a Shape and apply strokeBorder modifier inside CustomShapeView and return it as a view.
struct ContentView: View {
var body: some View {
CustomShapeView(shapeContent: {
RoundedRectangle(cornerRadius: 50.0)
})
.padding()
}
}
struct CustomShapeView<ShapeContent: Shape>: View {
let shapeContent: () -> ShapeContent
var body: some View {
return shapeContent()
//.strokeBorder(style: StrokeStyle(lineWidth: 10.0)) // <<: Here!!! Why?
}
}
Set InsettableShape generic constraint.
struct CustomShapeView<ShapeContent: InsettableShape>: View { // << == Here
let shapeContent: () -> ShapeContent
Just go to the strokeBorder definition, where strokeBorder is defined inside the InsettableShape protocol and which is inherited Shape.

Set a #State var inside an If statement in Swift

I am trying to set the value of a #State var in an If statement that is inside of a struct of type :View, like so:
struct Name: View {
#State someVar: Int = 0
var body: some View {
VStack {
if this > that {
someVar = 1
But when I do this I get the error: "Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols". If I use a class method that does what I need, like this:
if this > that {
someClass.doIt()
}
I get the same error.
What is the right way to do this?
You cannot put logic code like that inside your body -- all that you can have there is code that outputs a view.
So, you could do:
var body: some View {
if this > that {
Text("This")
} else {
Text("That")
}
}
because that results in a View (Text) getting rendered. In your example, though, you're just doing assignment.
That has to be done in a separate function or in a closure outside of what gets directly rendered in the view.
So:
func testThisThat() {
if this > that {
someVar = 1
}
}
var body: some View {
Button(action: {
testThisThat()
}) {
Text("Run test")
}
}
In the above, your logic runs in a closure outside the view hierarchy, and a Button gets rendered to the view.
If you give more specifics about what you're trying to do, perhaps the answer can be clarified, but that's the source of the error.
As suggested in the comments, you can also run logic code in onAppear, like this:
var body: some View {
VStack {
//view code
}.onAppear {
//logic
if this > that {
someVar = 1
}
}
}

SwiftUI - Xcode - Inferring type not possible for VStack

I am trying to create a simple master/detail app in Xcode.
I want that the detail view is
struct EditingView: View
{
var body: some View {
var mainVertical: VStack = VStack() //error here
{
var previewArea: HStack = HStack()
{
var editorButton: Button = Button()
//the same with return editorButton
// I have to add other controls, like a WKWebView
}
return previewArea
//this is a simple version, layout will have other stacks with controls inside
}
return mainVertical
}
}
but I get
Generic parameter 'Content' could not be inferred
The IDE offers me to fix but if I do that, it writes a generic type I have to fill but then other errors come, f.i. if I put AnyView o TupleView.
I would like that it infers everything, what is wrong that it cannot understand?
In SwiftUI you usually don't need to reference your controls. You can apply modifiers to them directly in the view.
This is the preferred way:
struct ContentView: View {
var body: some View {
VStack {
HStack {
Button("Click me") {
// some action
}
}
}
.background(Color.red) // modify your `VStack`
}
}
Alternatively if needed you can extract controls as separate variables:
struct ContentView: View {
var body: some View {
let hstack = HStack {
button
}
return VStack {
hstack
}
}
var button: some View {
Button("Click me") {
// some action
}
}
}
But in the end I definitely recommend you read Apple SwiftUI tutorials

How to define variables inside a GeometryReader in SwiftUI

I'd like to calculate the line width of a shape inside a view based on the view's size. Looking through various posts here on StackOverflow, I think the solution is to use a GeometryReader like this:
struct MyView: View {
var body: some View {
GeometryReader { geometry in
// Here goes your view content,
// and you can use the geometry variable
// which contains geometry.size of the parent
// You also have function to get the bounds
// of the parent: geometry.frame(in: .global)
}
}
}
My question is, how can I define variables inside the GeometryReader construct to be used for the view? I've tried to put a var statement directly after the line "GeometryReader { geometry in", but this gives a compiler error.
This seems to be a function builder related bug (as of Beta 3), and I recommend filing feedback on it.
The workaround I've been using is to use GeometryProxy in a separate method with an explicit return.
var body: some View {
GeometryReader { proxy in
self.useProxy(proxy)
}
}
func useProxy(_ proxy: GeometryProxy) -> some View {
var width = proxy.size.width
return VStack {
// use width in here
}
}