I am trying to define the following View in SwiftUI but it is not working:
struct ContentView: View {
var body: some View {
Text("Placeholder")
Button(action: {
// Do something
}) {
Text("Button")
}
}
}
The error is :
Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
There are also two warnings:
Result of 'Text' initializer is unused
and
Result of 'Button<Label>' initializer is unused
I am trying to code with XCode11 on Mac with OS Catalina. Does anybody know what the problem is?
You forgot to add mentioned VStack
var body: some View {
VStack { // << here !!
Text("Placeholder")
Button(action: {
// Do something
}) {
Text("Button")
}
}
}
You have missed the VStack :
var body: some View {
VStack {
Text("Placeholder")
Button("Button"){ }
}
}
Related
if I place 2 or more Lists with items in VStack or HStack and give the Vstack .shadow attribute, only the first List is scrollable and receives "gesture" events. Anybody has an idea why and is this intended behaviour? Seems like a bug to me. Tried on Xcode and device with iOS 14 and 14.+
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
ListView1()
ListView2()
}
.shadow(radius: 5)
}
}
struct ListView1:View {
var values=["1","2","3"]
var body: some View {
List {
ForEach(values, id: \.self) { (value) in
Text("Value \(value)")
}
}
}
}
struct ListView2:View {
var values=["5","6","7"]
var body: some View {
List {
ForEach(values, id: \.self) { (value) in
Text("Value \(value)")
}
}
}
}
Use compositingGroup() for solving issue!
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
ListView1()
ListView2()
}
.compositingGroup() // <<: Here!
.shadow(radius: 5)
}
}
I want to present the two destinations view in full screen mode from a single view.
Below is a sample of my code. Seem that the function only works for single presentation, if I have a second fullScreenCover defined, the first fullScreenCover didn't work properly.Is that any workaround at this moment?
import SwiftUI
struct TesFullScreen: View {
init(game : Int){
print(game)
}
var body: some View {
Text("Full Screen")
}
}
ContentView
import SwiftUI
struct ContentView: View {
#State var showFullScreen1 : Bool = false
#State var showFullScreen2 : Bool = false
var body: some View {
NavigationView {
VStack {
Spacer()
Button(action: { self.showFullScreen1 = true }) {
Text("Show Full Screen 1")
}
Button(action: { self.showFullScreen2 = true }) {
Text("Show Full Screen 2")
}
Spacer()
}
.navigationBarTitle("TextBugs", displayMode: .inline)
}
.fullScreenCover(isPresented: self.$showFullScreen1){
TesFullScreen(game: 1)
}
.fullScreenCover(isPresented: self.$showFullScreen2){
TesFullScreen(game: 2)
}
}
}
Not always the accepted answer works (for example if you have a ScrollView with subviews (cells in former days) which holds the buttons, that set the navigational flags).
But I found out, that you also can add the fullScreen-modifier onto an EmptyView. This code worked for me:
// IMPORTANT: Has to be within a container (e.g. VStack, HStack, ZStack, ...)
if myNavigation.flag1 || myNavigation.flag2 {
EmptyView().fullScreenCover(isPresented: $myNavigation.flag1)
{ MailComposer() }
EmptyView().fullScreenCover(isPresented: $myNavigation.flag2)
{ RatingStore() }
}
Usually some same modifier added one after another is ignored. So the simplest fix is to attach them to different views, like
struct FullSContentView: View {
#State var showFullScreen1 : Bool = false
#State var showFullScreen2 : Bool = false
var body: some View {
NavigationView {
VStack {
Spacer()
Button(action: { self.showFullScreen1 = true }) {
Text("Show Full Screen 1")
}
.fullScreenCover(isPresented: self.$showFullScreen1){
Text("TesFullScreen(game: 1)")
}
Button(action: { self.showFullScreen2 = true }) {
Text("Show Full Screen 2")
}
.fullScreenCover(isPresented: self.$showFullScreen2){
Text("TesFullScreen(game: 2)")
}
Spacer()
}
.navigationBarTitle("TextBugs", displayMode: .inline)
}
}
}
Alternate is to have one .fullScreenCover(item:... modifier and show inside different views depending on input item.
The only thing that worked for me was the answer in this link:
https://forums.swift.org/t/multiple-sheet-view-modifiers-on-the-same-view/35267
Using the EmptyView method or other solutions always broke a transition animation on one of the two presentations. Either transitioning to or from that view and depending on what order I chose them.
Using the approach by Lantua in the link which is using the item argument instead of isPresented worked in all cases:
enum SheetChoice: Hashable, Identifiable {
case a, b
var id: SheetChoice { self }
}
struct ContentView: View {
#State var sheetState: SheetChoice?
var body: some View {
VStack {
...
}
.sheet(item: $sheetState) { item in
if item == .a {
Text("A")
} else {
Text("B")
}
}
}
}
The sheetState needs to be optional for it to work.
I'm pretty sure this is a bug in SwiftUI, but I wondered if anyone has encountered it and figured out a workaround. My normal use case is to have a search field appear, but I've simplified it to the point where a simple text string exhibits the bug.
Create a single-view app, copy this into ContentView, and run it. Tap the search icon twice, then scroll the view; you'll see the text scrolling UNDER the title.
import SwiftUI
struct ContentView: View {
private var items = (0 ... 50).map {String($0)}
#State private var condition = false
var searchButton: some View {
Button(action: {self.condition.toggle()}) {
Image(systemName: "magnifyingglass").imageScale(.large)
}
}
var body: some View {
NavigationView {
VStack {
if condition {
Text("Peekaboo")
}
List {
ForEach(items, id: \.self) {item in
HStack {
Text(item)
}
}
}
}
.navigationBarTitle("List of Items")
.navigationBarItems(leading: searchButton)
}
}
}
Maybe it is a bug, submit feedback to Apple, but currently this is how NavigationView behaves - it collapses navigation bar only if its top content is List/ScrollView/Form. So to solve the issue move your VStack either into a List or out of NavigationView
1)
var body: some View {
NavigationView {
List {
if condition {
Text("Peekaboo")
}
ForEach(items, id: \.self) {item in
2)
var body: some View {
VStack {
if condition {
Text("Peekaboo")
}
NavigationView {
List {
It seems that a View cannot cope with variable number of views.
A workaround this strange behavior is this:
import SwiftUI
struct ContentView: View {
private var items = (0 ... 50).map {String($0)}
#State private var condition = false
var searchButton: some View {
Button(action: {self.condition.toggle()}) {
Image(systemName: "magnifyingglass").imageScale(.large)
}
}
var body: some View {
NavigationView {
VStack {
if condition {
Text("Peekaboo")
} else {
Text("")
}
// or use this Text(condition ? "Peekaboo" : "")
List {
ForEach(items, id: \.self) {item in
HStack {
Text(item)
}
}
}
}
.navigationBarTitle("List of Items")
.navigationBarItems(leading: searchButton)
}
}
}
Let me know if it works, if not let us know what device/system you are using. Tested with Xcode 11.6 beta, Mac 10.15.5, target ios 13.5 and mac catalyst.
I have two views ListView and DetailView
ListView:
#EnvironmentObject var userData: UserData
var body: some View {
VStack {
ForEach(userData.packs) { pack in
if pack.added {
NavigationLink(destination: DetailView(packIndex: self.userData.packs.firstIndex(where: { $0.id == pack.id })!)) {
MyRowViewDoesntMatter(pack: pack)
}
}
}
}
.padding(.horizontal)
}
DetailView:
#EnvironmentObject var userData: UserData
var packIndex: Int
VStack {
List {
VStack {
.... some Vies ... doesn't matter
.navigationBarItems(trailing:
THE PROBLEM IS HERE (BELOW)
Button(action: {
self.userData.packs[self.packIndex].added.toggle()
}) {
Image(systemName: self.userData.packs[self.packIndex].added ? "plus.circle.fill" : "plus.circle")
}
...
The problem is when I click on button in the navigationBarItems in DetailView. The "added" property of the "#EnvironmentObject var userData: UserData" is updated and the user's screen is going back (to the RowView). I fond out that the problem with EnvironmentObject, because the data is updated and View tries to rerender (?) that is why it pushes me back?
How to fix it? I want to stay at the DetailView screen after clicking the button.
P.S. I need to use EnvironmentObject type because then when I go back I need to see the results.
Thank you very much!
Here is possible approach (by introducing some kind of selection). As NavigationView does not allow to remove link from stack (as identifier of stacked navigation), probably also worth considering separate view model for DetailView to be applied into common container on finish editing.
Tested with Xcode 11.4 / iOS 13.4.
Some replication of your code, used for testing:
struct ListView: View {
#EnvironmentObject var userData: PushBackUserData
#State private var selectedPack: Pack? = nil
var body: some View {
NavigationView {
VStack {
ForEach(Array(userData.packs.enumerated()), id: \.element.id) { i, pack in
NavigationLink("Pack \(pack.id)", destination:
DetailView(pack: self.$selectedPack)
.onAppear {
self.selectedPack = pack
}
.onDisappear {
self.userData.packs[i].added = self.selectedPack?.added ?? false
}
).isHidden(!pack.added)
}
}
.padding(.horizontal)
}
}
}
struct DetailView: View {
#Binding var pack: Pack?
var body: some View {
VStack {
List {
VStack {
Text("Pack \(pack?.id ?? "<none>")")
}
}
.navigationBarItems(trailing:
Button(action: {
self.pack?.added.toggle()
}) {
Image(systemName: pack?.added ?? false ? "plus.circle.fill" : "plus.circle")
}
)
}
}
}
just convenient helper extension
extension View {
func isHidden(_ hidden: Bool) -> some View {
Group {
if hidden { self.hidden() }
else { self }
}
}
}
I've got a very simple VStack, based directly off of one of Paul Hudson's excellent SwiftUI samples. There are two lines of Text, one hidden. There's a method to toggle an #State var which controls the hidden Text.
If I call that function from within the VStack, it animates properly. If I call it from a navigationBarItems, it loses the animation. Am I missing something about how views are composed?
struct ContentView: View {
#State var showDetails = false
func toggleDetails() { withAnimation { self.showDetails.toggle() } }
var body: some View {
NavigationView() {
VStack {
Button(action: { self.toggleDetails() }) { Text("Tap to show details") }
if showDetails { Text("Details go here.") }
}
.navigationBarTitle(Text("Nav Bar"))
.navigationBarItems(trailing:
Button(action: { self.toggleDetails() }) {
Text("Details")
})
}
}
}
#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif
Beta 5 Update
It seems beta 5 fixed this problem. Workaround no longer needed.
Workaround for beta 4 and previous versions
I think the reason it does not work, is because you are calling withAnimation from a different branch of the view tree. The "Details" button and the views that need to be animated are on different branches of the hierarchy. I am just guessing, but it seems to be supported by the workaround I posted here.
If instead of using explicit animations (i.e., withAnimation), you use implicit animations on both the VStack and the Text, it works:
struct ContentView: View {
#State var showDetails = false
func toggleDetails() { self.showDetails.toggle() }
var body: some View {
NavigationView() {
VStack {
Button(action: { self.toggleDetails() }) { Text("Tap to show details") }
if showDetails {
Text("Details go here.").animation(.basic())
}
}
.animation(.basic())
.navigationBarTitle(Text("Nav Bar"))
.navigationBarItems(trailing:
Button(action: {
self.toggleDetails()
}) { Text("Details") })
}
}
}