I'm building a custom view and I'm trying to manipulate the active state of its children.
I have this:
struct Menu<Content>: View where Content: View {
#ViewBuilder var content: () -> Content
var body: some View {
content()
}
}
struct ScreenView: View {
var body: some View {
Menu {
Text("Home")
Text("Settings")
Text("Profile")
}
}
}
I would like to be able to pass a binding to the Menu view and based on that, to change the text color if the state is matching the actual text or id of the view. There is a Picker view example that does what I want to achieve. The Picker is managing the look and feel of the selected element. Am I wrong?
struct PickerViewExample: View {
#State var selection: Int
var body: some View {
Picker(selection: $selection, label: Text("Picker"), content: {
Text("1").tag(1)
Text("2").tag(2)
})
}
}
I'd like to know if there is a way to ForEach somehow the content ViewBuilder property in order to manipulate subviews. I like the way Apple solved this using tags. Any alternative is welcome.
I think, this will help
struct ContentView: View {
private var menuItem = ["Home", "Settings", "Profile"]
#State private var selectedMenu = "Home"
var body: some View {
VStack {
Picker("Menu", selection: $selectedMenuIndex, content: {
ForEach(menuItem, id: \.self, content: { title in
Text(title)
})
})
Text("Selected menu: \(selectedMenu)")
}
}
}
Related
I'm new to swiftUI here and I want to try out to pass data between two views. But it doesn't seem to work.
I'm using Xcode 13.2 & iOS 15 for the simulator.
This is my code for the first view:
struct ContentView: View {
#State var myName: String = ""
var body: some View {
NavigationView {
VStack {
TextField("Enter your name", text: $myName)
Text(self.myName)
NavigationLink(destination: BView(myName: self.$myName), label: {
Image(systemName: "arrowshape.turn.up.left")
})
}//: VSTACK
.padding(.horizontal, 20)
}//:NAVIGATION VIEW
}
}
This is code for the second view:
struct BView: View {
#Binding var myName: String
var body: some View {
NavigationView {
Text("BView")
Text(self.myName)
}//:NAVIGATION VIEW
}
}
I want myName to be input in the first page which is ContentView() and then pass down the input data to BView().
Unfortunately, once I run it on the simulator, the input data doesn't;t show up.
Your code is fine just add VStack in BView.
struct BView: View {
#Binding var myName: String
var body: some View {
NavigationView {
VStack { // HERE
Text("BView")
Text(self.myName)
}
}//:NAVIGATION VIEW
}
}
Please use #EnvironmentObject to pass the data to view.
https://developer.apple.com/documentation/swiftui/environmentobject
I am presenting a sheet that will present a slider within a section which is within a form. I was trying to add my subview AddDoujin into this view as well. But the problem is, is that both views are scrollable and aren't layering on top of each other and seems like it is being added to the bottom (if that makes sense). My goal is to make the AddDoujin view right under the slider rather than both being each their own view and both of them being scrollable. Sorry if this is unclear and you need more information.
import SwiftUI
struct TestingAddDoujin: View {
//Varaibles
#State private var InputDoujin:String = ""
var DoujinApi:DoujinAPI
#Binding var isPresented:Bool
#State private var RedoEntry:Bool = false
var PickerOptions = ["Doujin", "Hentai"]
#State var PickerSelected = ""
#State var CurrentSelectionForPicker = 0
var body: some View {
GeometryReader { geo in
NavigationView{
VStack {
ZStack{
Form{
Section(header: Text("What you you looking for?")) {
Picker(selection: $CurrentSelectionForPicker, label: Text("Please select one")) {
ForEach(0..<PickerOptions.count) {
Text("\(self.PickerOptions[$0])")
}
}
.pickerStyle(SegmentedPickerStyle())
}
}
}
AddDoujin(DoujinApi: DoujinApi, isPresented: $isPresented)
}
}
}
}
}
struct TestingAddDoujin_Previews: PreviewProvider {
static var previews: some View {
TestingAddDoujin(DoujinApi: DoujinAPI(), isPresented: .constant(false))
}
}
It seems like if you just move AddDoujin(DoujinApi: DoujinApi, isPresented: $isPresented) right below .pickerStyle(SegmentedPickerStyle()) You will get the desired result.
I'm guessing you've tried this. So maybe show and tell us more about what you are trying to accomplish.
btw. In Swift it's not convention to capitalize the first letter of a variable.
This question is identical to SwiftUI #Binding update doesn't refresh view, but the accepted answer is not applicable for my case.
The accepted answer says
A View using a #Binding will update when the underlying #State change, but the #State must be defined within the view hierarchy. (Else you could bind to a publisher)
In my case, the view hierarchy doesn't have the view which is having the #State. The view having the binding is presented modally to the user.
To summarize the issue again
I want to create a view, similar to Toggle which initializes from a Binding. This view will show the contents from the wrapped value and as it performs the updates, the original storage of the value will get updated automatically.
As I have learnt, updating the #Binding in a view, doesn't invalidate it. Then how to implement such a view.
Also I can't depend on the parent view to eventually update this view, because the view is shown on a modally presented screen.
I don't want to use workarounds like using a #State to explicitly trigger a refresh. So what is the correct way to implement such a view.
Code example
The view TextModifier takes a Binding. The view does some modifications to the view. For now it just appends "_Updated" to the value passed.
I initialize the view as TextModifier(text: <some_binding_var>)
struct TextModifier: View {
#Binding var text: String
var body: some View {
Text(text)
.onTapGesture {
text += "_Updated"
}
}
}
This view shows the text and on tapping it updates it in the original source, but as expected the view doesn't update itself on tapping.
So, how to implement this view so that it also updates itself when it updates the binding value.
The accepted answer to the linked question also says
Else you could bind to a publisher
I don't know how to do this. Does anybody know how to implement this and also provide a code example. Thanks.
Updated with full code and gif
struct ContentView: View {
#ObservedObject var viewModel = TestViewModel()
var body: some View {
List {
ForEach(viewModel.itemsList, id: \.self) { item in
ItemView(text: $viewModel.itemsList[getItemIndex(item)])
}
}
}
private func getItemIndex(_ item: String) -> Int {
viewModel.itemsList.firstIndex { $0 == item }!
}
}
class TestViewModel: ObservableObject {
#Published var itemsList = ["Item 1", "Item 2", "Item 3"]
}
struct ItemView: View {
#Binding var text: String
#State private var showEditorView = false
var body: some View {
Text(text)
.onTapGesture {
showEditorView = true
}
.sheet(isPresented: $showEditorView) {
TextModifier(text: $text, showView: $showEditorView)
}
}
}
struct TextModifier: View {
#Binding var text: String
#Binding var showView: Bool
var body: some View {
VStack(spacing: 20) {
Text("Tap on the text to update it")
.foregroundColor(.blue)
Text(text)
.onTapGesture {
text += "_Updated"
}
Button {
showView = false
} label: {
Text("Dismiss")
.foregroundColor(.blue)
}
}
}
}
what I am trying to achieve is creating a hierarchical view. I understand that iOS simply doesn't like to use breadcrumbs but I need to navigate from a main view in to deeper subviews. they need to be nested and infinite.
you can see what I've done so far in the code and gif below. As I'm a beginner developer I'm not sure if this is the right way to achieve this kind of structure (infinite sub-views nested inside sub-views). Also when I navigate back in views, added buttons(struct A) disappears. What seems to be the problem?
Thanks in advance!
code in action gif
import SwiftUI
struct A: View, Identifiable {
#EnvironmentObject var documentB: classB
var id: Int
var text: String
var destinationLink: B?
var body: some View {
NavigationLink(destination: self.destinationLink) {
VStack{
Rectangle()
.frame(width: 35, height:25)
.background(Color.red)
Text("\(text)")
}
}
}
}
struct B: View, Identifiable {
#EnvironmentObject var documentB: classB
#State var arrayA: [A] = []
var id: Int
var text: String
var mainText: String = "Placeholder"
var body: some View {
NavigationView {
VStack {
Spacer()
ForEach(arrayA){ item in
item
}
Spacer()
Button(action: {
let newB = B(id:self.documentB.arrayB.count+1, text:"B \(self.documentB.arrayB.count+1)")
self.documentB.arrayB.append(newB)
self.arrayA.append(A(id:self.arrayA.count+1, text:"AA \(self.arrayA.count+1)", destinationLink: newB))
}) {
Text("Add A \(self.arrayA.count), B Count: \(self.documentB.arrayB.count)")
}
}
.navigationBarTitle(text)
}
}
}
class classB: ObservableObject {
#Published var arrayB: [B] = [B(id:1, text:"MainView")]
}
struct ContentView: View {
#ObservedObject var documentB = classB()
var body: some View {
VStack {
documentB.arrayB[0]
}
.environmentObject(documentB)
}
}
You just need to move NavigationView into ContentView, because the only one is needed on one view hierarchy, so
struct ContentView: View {
#ObservedObject var documentB = classB()
var body: some View {
NavigationView { // << move it here from B
VStack {
documentB.arrayB[0]
}
}
.environmentObject(documentB)
}
}
I need to perform an action similar to "unwind to segue" when the user taps and select a cell row in a list. I'm new to SwiftUI and I would really appreciate if someone can help me.
struct cellView : View {
#State var name = ""
var body: some View {
VStack {
Text(name)
Button(action: { ***unwind to parent view code*** })
}
}
}
I expect there to be an action code to return to parent view after tapping the cell row.
You can do it using Enviroment variable called presenationMode, like this:
struct CellView : View {
#Environment(\.presentationMode) var presentationMode
#State var name = ""
var body: some View {
VStack {
Text(name)
Button("Tap me",
action: { self.presentationMode.wrappedValue.dismiss() })
}
}
}