SwiftUI PageView update outside view - swift

I have a PageStyle TabView, and want to update a Text that is outside of it.
I've used onAppear to get the page change event, and looks good at first if I scroll forward, but once I go backwards a few onAppear are missed.
Is this the correct way of doing this. Is is possible to do that?
struct PageView: View {
#State var title: String = "hello"
var body: some View {
VStack {
Text(self.title)
TabView {
ForEach(0..<100) { i in
Text("\(i)").onAppear {
self.title = ("TITLE = \(i)")
}
}
}
.tabViewStyle(PageTabViewStyle())
}
}
}

You can do this by using TabView(selection: and tag modifier on Text items, like below (tested with Xcode 12.1 / iOS 14.1)
var body: some View {
VStack {
Text(self.title)
TabView(selection: $title){ // << here !!
ForEach(0..<100) { i in
Text("\(i)")
.tag(String(i)) // << here !!
}
}
.tabViewStyle(PageTabViewStyle())
}
}

Related

NavigationBar won't collapse with TabView iOS 15

Not sure if this is a bug, but this is how to reproduce it in iOS 15 (didn't try lower versions)
In tab 1, scroll up (navigation bar will collapse properly)
Now, tap tab 2, but do not scroll the list
go back to tab 1. Now you should see navigation bar failed to collapse.
Source code:
struct TestNavigationView: View {
var body: some View {
NavigationView {
TabView {
LongList()
.tabItem({Text("Tab 1")})
LongList()
.tabItem({Text("Tab 2")})
}
.navigationTitle("Navigation Bar Test")
}
}
}
struct LongList: View {
var list = (1..<50).map {$0}
var body: some View {
List(list, id: \.self) { item in
NavigationLink {
Text("New view")
} label: {
Text("Item \(item)")
}
}
}
}
I have tried to twist the styles, but nothing worked. Any help would be appreciated! Thank you!
I believe that TabView and NavigationView are not good friends with each other.
If you avoid having the NavigationView around the TabView, you can achieve the same result. Move it to LongList, together with the .navigationTitle() modifier.
The following works fine:
struct TestNavigationView: View {
var body: some View {
// NavigationView {
TabView {
LongList(text: "One")
.tabItem({Text("Tab 1")})
LongList(text: "Two")
.tabItem({Text("Tab 2")})
}
// }
}
}
struct LongList: View {
var list = (1..<50).map {$0}
let text: String
var body: some View {
NavigationView {
List(list, id: \.self) { item in
NavigationLink {
Text("New view")
} label: {
Text("Item \(item) - \(text)")
}
}
.navigationTitle("Navigation Bar Test")
}
}
}

NavigationLink inside a List and VStack does not work after the first pop

I am trying to use the new Pull to Refresh feature in the latest version of SWiftUI which requires a List. Enclosing the VStack in a List causes the NavigationLink to work only once. Below is a simple version of the code without the Pull To Refresh part.
There is a question that was asked 68144891 on stackoverflow and there was a refrence to a known issue link which takes you to a page not found (https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-15-beta-release-notes)
Steps o reproduce
Tap "Press Me 1" or one of the items
Tap "Show Details"
Tap Back at the top
Tap "Press Me" again will not navigate to the next screen. A grey screen blocks when you tap
The app works without the VStack
struct ContentView: View {
var body: some View {
NavigationView {
List {
VStack { // commenting VStack works
Text("Options").font(.largeTitle).bold()
ForEach(1..<5, id:\.self) { counter in
NavigationLink(destination: SubView(counter: counter)) {
Text("Press Me \(counter)").font(.headline)
}
.buttonStyle(PlainButtonStyle())
}
}
}.listStyle(.grouped)
}
}
}
struct SubView: View {
var counter: Int
#State private var showDetails = false
var body: some View {
VStack(alignment: .leading) {
Button("Show details") {
showDetails.toggle()
}
if showDetails {
Text("Clicked")
.font(.largeTitle)
}
}
}
}
Any help appreciated
Thanks much!
... follow-up to my comment
I assume you wanted this
struct ContentView: View {
var body: some View {
NavigationView {
List {
Section(Text("Options").font(.largeTitle).bold()) {
ForEach(1..<5, id:\.self) { counter in
NavigationLink(destination: SubView(counter: counter)) {
Text("Press Me \(counter)").font(.headline)
}
.buttonStyle(PlainButtonStyle())
}
}
}.listStyle(.grouped)
}
}
}

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

Extra elements for SwiftUI destination view

Can someone explain why there are 2 UI elements(UINavigationBarContentView, UINavigationBarLargeTitleView) between the Image(the blue rectangle) and navigation bar title(text One)?
The code I use is this:
let item: ImageNameModel
#State private var image: Image?
var body: some View {
NavigationView {
VStack {
if image != nil {
image?.resizable().scaledToFit()
} else {
Text("Image not loaded")
}
}
.padding([.horizontal, .bottom])
}
.navigationBarTitle(Text(item.name))
.onAppear(perform: loadImage)
}
It's probably because of the extra NavigationView.
You don't need to have a NavigationView wrapping your destination.
For example, with this sample code here:
struct RootView: View {
var body: some View {
NavigationView {
NavigationLink(destination: DestinationView()) {
Text("Click here")
}.navigationBarTitle(Text("Title"))
}
}
}
struct DestinationView: View {
var body: some View {
NavigationView {
Text("Destination")
.navigationBarTitle(Text("Destination"))
}
}
}
I get a result like this:
But If I remove the NavigationView from DestinationView, I get the result that is probably what you expect:

SwiftUI: Dismiss View Within macOS NavigationView

As detailed here (on an iOS topic), the following code can be used to make a SwiftUI View dismiss itself:
#Environment(\.presentationMode) var presentationMode
// ...
presentationMode.wrappedValue.dismiss()
However, this approach doesn't work for a native (not Catalyst) macOS NavigationView setup (such as the below), where the selected view is displayed alongside the List.
Ideally, when any of these sub-views use the above, the list would go back to having nothing selected (like when it first launched); however, the dismiss function appears to do nothing: the view remains exactly the same.
Is this a bug, or expected macOS behaviour?
Is there another approach that can be used instead?
struct HelpView: View {
var body: some View {
NavigationView {
List {
NavigationLink(destination:
AboutAppView()
) {
Text("About this App")
}
NavigationLink(destination:
Text("Here’s a User Guide")
) {
Text("User Guide")
}
}
}
}
}
struct AboutAppView: View {
#Environment(\.presentationMode) var presentationMode
public var body: some View {
Button(action: {
self.dismissSelf()
}) {
Text("Dismiss Me!")
}
}
private func dismissSelf() {
presentationMode.wrappedValue.dismiss()
}
}
FYI: The real intent is for less direct scenarios (such as triggering from an Alert upon completion of a task); the button setup here is just for simplicity.
The solution here is simple. Do not use Navigation View where you need to dismiss the view.
Check the example given by Apple https://developer.apple.com/tutorials/swiftui/creating-a-macos-app
If you need dismissable view, there is 2 way.
Create a new modal window (This is more complicated)
Use sheet.
Following is implimenation fo sheet in macOS with SwiftUI
struct HelpView: View {
#State private var showModal = false
var body: some View {
NavigationView {
List {
NavigationLink(destination:
VStack {
Button("About"){ self.showModal.toggle() }
Text("Here’s a User Guide")
}
) {
Text("User Guide")
}
}
}
.sheet(isPresented: $showModal) {
AboutAppView(showModal: self.$showModal)
}
}
}
struct AboutAppView: View {
#Binding var showModal: Bool
public var body: some View {
Button(action: {
self.showModal.toggle()
}) {
Text("Dismiss Me!")
}
}
}
There is also a 3rd option to use ZStack to create a Modal Card in RootView and change opacity to show and hide with dynamic data.