This code works very well for me. But I need to add 2 more .sheet in this page. When I try to other solutions list cell doesnt pass to data correctly. How do I improve this code for 3 sheet?
#State var selectedUser: User?
List...
UserCell(user: user)
.onTapGesture {
self.selectedUser = user
}
.sheet(item: self.$selectedUser) { user in
DetailView(user: user)
}
NavigationView has only one sheet per view, so data in sheet instead multiple sheets as in your list;
add one sheet to your view change data on tap, like in below code;
enum SheetType {
case preview
case edit
case yourAnyChoice
}
struct ContentView:View{
#State var selectedUser:String = ""
#State var showingDetail = false
#State var sheetType:SheetType = SheetType.preview
var body: some View {
List(userList){in user
HStack{
Button(action: {
self.selectedUser = user.name
self.sheetType = .preview
self.showingDetail.toggle()
}){
Text("name")
}
Button(action: {
self.selectedUser = user.name
self.sheetType = .edit
self.showingDetail.toggle()
}){
Text("edit")
}
Button(action: {
self.selectedUser = user.name
self.sheetType = .yourAnyChoice
self.showingDetail.toggle()
}){
Text("yourAction")
}
}
}
.sheet(isPresented: self.$showingDetail){
detailView(text:self.$selectedUser,type:self.$sheetType)
}
}
struct detailView:View {
#Binding var text:String
#Binding var type:SheetType
var body:some View{
if type == SheetType.preview{
Text(text)
}
if type == .edit {
yourEditingView() // as per your requirements
}
if type == SheetType.yourAnyChoice{
yourChoiceViews()
}
}
Related
I am trying to get data from one view to another.
I can not figure out how to get values from the fourth view array into the Third view.
I am not using storyboards. I tried using #EnvironmentObject but can not make it work. New to coding. In xcode I am using watchos without app.
I tried to strip out most of the code and leave just the important stuff that can be tested. I used NavigationLink(destination: )to transfer between views.
enter code here
class viewFromEveryWhere: ObservableObject {
#Published var testname2: String = "testTTname"
}
struct secondView: View {
var body: some View {
Text("second view")
List(1..<7) {
Text("\($0)")
}
}
}
struct thirdView: View {
#EnvironmentObject var testname2: viewFromEveryWhere
#EnvironmentObject var testSixTestArray: viewFromEveryWhere
#State var sixTestArray:[String] = ["ww","GS","DW","CD","TS","JW",]
var body: some View {
List(sixTestArray, id:\.self) {
Text($0)
}
}
}
struct fourthView: View {
#StateObject var testname2 = viewFromEveryWhere()
#State private var name: String = ""
#State var testSixTestArray:[String] = []
func collectName () {
print("collectName triggered")
if testSixTestArray.count < 5 {
// testSixTestArray.append(name)
print(name)
print(testSixTestArray)
}
// .enviromentObject(testSixTestArray)
}
var body: some View {
VStack(alignment: . leading) {
Text("Type a name")
TextField("Enter your name", text: $name)
Text("Click to add, \(name)!")
// Button("click this if \(name) is correct") {}
Button(action:{
print("Button Tapped")
collectName()
print(testSixTestArray.count)
name = ""
}) {
Text("Add \(name) to list")
}
// .buttonStyle(GrowingButton1())
}
Text("forth view")
// testSixTestArray.append(name)
.environmentObject(testname2)
}
}
/*func presentTextInputControllerWithSuggestions(forLanguage suggestionsHandler:
((String)-> [Any]?)?,
allowedInputMode inputMode:
WKTextInputMode,
completion: #escaping ([Any]?) -> Void) {}
*/
struct ContentView: View {
#State var sixNameArray:[String] = ["","","","","","",]
#State var messageTextBox: String = "Start"
#State var button1: String = "Button 1"
#State var button2: String = "Button 2"
#State var button3: String = "Button 3"
var body: some View {
NavigationView {
VStack{
Text(messageTextBox)
.frame(width: 120, height: 15, alignment: .center)
.truncationMode(.tail)
.padding()
NavigationLink(destination: secondView(),
label:{
Text(button1)
})
.navigationBarTitle("Main Page")
NavigationLink(destination: thirdView(),
label:{
Text(button2)
})
NavigationLink(destination: fourthView(),
label:{
Text(button3)
})
}
}
}
}
enter code here
I'm having a weird problem that I can't seem to figure out with SwiftUI. I have a TabView in my ContentView, there are 3 tabs (chat list, user list, and profile) the app loads up on the chat list tab. The problem is, when I select the second tab (user list) it goes to that tab for a split second, then goes right back to the chat list. It doesn't make any sense to me. The 2 main tabs make an API call to get information from a server, and everything is working great, except that first click.
The app loads up, I click the user list tab and it shows for a split second, then goes back to the chat list tab. I can then click the user list tab again and it will go to that tab and stay there, but the first click on that tab always sends you back to the chat list tab.
I'll post up some of my code, hopefully someone will be able to see what I'm doing wrong, because I sure can't.
ContentView
struct ContentView: View {
#Environment(\.managedObjectContext) private var viewContext
#State var selectedTab = 0
#State var setup: Bool = false
#State var notificationChatID: String = ""
#ObservedObject var userModel: UserModel = UserModel()
#ObservedObject var chatModel: ChatModel = ChatModel()
#ObservedObject var appState: AppState = AppState.shared
var pushNavigationBinding : Binding<Bool> {
.init { () -> Bool in
appState.selectedChatID != nil
}
set: { (newValue) in
if !newValue {
appState.selectedChatID = nil
}
}
}
let settings = UserDefaults.standard
var body: some View {
ZStack {
if setup {
TabView(selection: $selectedTab) {
ChatList(launchedChatID: appState.selectedChatID ?? "", userModel: userModel, chatModel: chatModel)
.tabItem {
Image(systemName: "message.circle.fill")
Text("Active Chats")
}
UserList(userModel: userModel)
.tabItem {
Image(systemName: "person.3.fill")
Text("User List")
}
ProfileView()
.tabItem {
Image(systemName: "person.crop.circle")
Text("Profile")
}
}
} else {
Onboarding(userModel: userModel, isSetup: $setup)
}
}
.onReceive(NotificationCenter.default.publisher(for: Notification.Name.NewMessage), perform: { notification in
if let info = notification.userInfo {
let chatID = info["chatID"] as? String ?? ""
if chatID != "" {
chatModel.selectedChat = chatID
appState.selectedChatID = chatID
self.notificationChatID = chatID
}
}
})
}
Then my UserList
struct UserList: View {
#ObservedObject var userModel: UserModel
#ObservedObject var chatModel: ChatModel = ChatModel()
#State var action: Int? = -1
var body: some View {
NavigationView {
VStack {
List {
ForEach(0..<userModel.arrayOfUsers.count, id: \.self) { index in
ZStack(alignment: .leading) {
NavigationLink(
destination: ChatView(model: chatModel, userModel: userModel, item: $action),
tag: index,
selection: $action
) {
EmptyView().frame(width: .zero, height: .zero, alignment: .center)
}
Button(action: {
print("You selected \(userModel.arrayOfUsers[index].name)")
userModel.selectedUserName = userModel.arrayOfUsers[index].name
userModel.selectedUserID = userModel.arrayOfUsers[index].id
self.action = index
}) {
Text(userModel.arrayOfUsers[index].name)
}
}
}
}
Spacer()
}.navigationBarTitle(Text("Users"), displayMode: .inline)
}.onAppear() {
print("Inside the userlist on appear")
if userModel.arrayOfUsers.count == 0 {
ApiService.getUsers() { (res) in
switch(res) {
case .success(let response):
print("In success")
let users = response!.users!
DispatchQueue.main.async {
for user in users.users {
userModel.arrayOfUsers.append(user)
}
}
break
case .failure(let error):
print("Error getting users")
print(error)
break
}
}
}
}
}
}
My userModel.arrayOfUsers is #Published
UserModel
class UserModel: ObservableObject {
var name: String = ""
var id: String = ""
var myUserID: String = ""
#Published var arrayOfUsers: [User] = []
var selectedUserID: String = ""
var selectedUserName: String = ""
}
In the console in Xcode I see
Inside the userlist on appear
...(network stuff)
In the success
In the on appear in ChatList
So it's loading the UserList, it shows the network call out to my API, it shows the In the success from the API call in the UserList, then the very next thing is back to the In the on appear in ChatList I can't figure out why it's kicking me back to the chat list.
You're binding your TabView's current tab to $selectedTab, but not providing SwiftUI with any information on how to alter that value when the user changes tabs. And so, because selectedTab hasn't changed, when the drawing system comes to review your view structure, it still concludes that you want to see the first tab.
You should add a .tag modifier after each .tabItem to tell SwiftUI what values represent each tab. Then, when the user selects each tab, selectedTab will be updated and the tab choice will "stick".
For example:
TabView(selection: $selectedTab) {
ChatList(launchedChatID: appState.selectedChatID ?? "", userModel: userModel, chatModel: chatModel)
.tabItem {
Image(systemName: "message.circle.fill")
Text("Active Chats")
}
.tag(0)
UserList(userModel: userModel)
.tabItem {
Image(systemName: "person.3.fill")
Text("User List")
}
.tag(1)
ProfileView()
.tabItem {
Image(systemName: "person.crop.circle")
Text("Profile")
}
.tag(2)
}
Note that unless you're persisting the user's choice in some way (e.g., by declaring your state variable with #SceneStorage) you can get the same effect by not using a selection argument at all.
I created a custom alert. I want the product to be added to the basket when the Ok button on Alert is clicked on the first screen. When the Ok button is pressed on the second screen, the purchase of the product is requested. I called the same alert on 2 pages and I want it to take different actions. I couldn't do that with #Escaping.
AlertView
struct AlertView: View {
#Binding var openShowAlert: Bool
#State var closeShowAlert: Bool = false
#State var openState: CGFloat = -UIScreen.main.bounds.height
#State var closeState: CGFloat = UIScreen.main.bounds.height
var title: String = ""
var message: String = ""
var okButtonText: String = ""
var cancelButtonText: String = ""
var body: some View {
VStack {
Text(title)
.michromaFont(size: 20)
.padding(.top)
Spacer()
Text(message)
.michromaFont(size: 18)
Spacer()
HStack {
Button(action: {
self.openShowAlert = false
openState = -UIScreen.main.bounds.height
closeState = UIScreen.main.bounds.height
}) {
Text(cancelButtonText)
.foregroundColor(.red)
}
Spacer()
Button(action: {}) {
Text(okButtonText)
}
}
.michromaFont(size: 18)
.padding([.horizontal, .bottom])
}
.neumorphisimBackground(width: 300, height: 200)
.offset(y: self.openShowAlert ? self.openState : self.closeState)
.animation(.easeInOut)
.onChange(of: self.openShowAlert, perform: { value in
if value {
self.openState = .zero
}
})
}
}
DetailView
On this screen, click Alert presentation to add the product to the cart.
struct DetailView: View {
#Environment(\.presentationMode) var presentationMode
var device = UIDevice.current.userInterfaceIdiom
#State var width: CGFloat = 300
#State var height: CGFloat = 450
#Binding var text: String
#State var showAlert: Bool = false
var body: some View {
ZStack() {
......
AlertView(openShowAlert: self.$showAlert)
}
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
CartView Click I am providing an alert on this screen to purchase the product.
struct CartView: View {
#State var cartList = [1,2,3,4,5,6,7,8,9]
#Environment(\.presentationMode) var presentationMode
#State var showAlert: Bool = false
var body: some View {
ZStack(alignment: .top) {
.....
AlertView(openShowAlert: self.$showAlert)
}
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
How can I send two different actions in the same alert.
Hmm, I don't see why it shouldn't work with a closure. Have you tried passing over a closure like so?
struct AlertView: View {
...
var okButtonAction: () -> ()
var body: some View {
...
Button(action: okButtonAction) {
Text(okButtonText)
}
}
}
Usage
AlertView(openShowAlert: self.$showAlert) {
// Your custom code
}
Alternative Idea
You could work with Combine and create a publisher with a specific key to identify the sender screen. Then you can put your custom code inside .onReceive().
Scenario:
I have a simple picker within a form.
I select a picker item (with chevron) from the form row.
I choose an item (row) from a list of items in the result panel.
The result panel slides away to reveal the original panel.
I am NOT able to repeat this procedure.
Here's my code:
class ChosenView: ObservableObject {
static let choices = ["Modal", "PopOver", "Circle", "CircleImage", "Scroll", "Segment", "Tab", "Multi-Line"]
#Published
var type = 0
}
struct ContentView: View {
#ObservedObject var chosenView = ChosenView()
#State private var isPresented = false
var body: some View {
VStack {
NavigationView {
Form {
Picker(selection: $chosenView.type, label: Text("The Panels")) {
ForEach(0..<ChosenView.choices.count) {
Text(ChosenView.choices[$0]).tag($0)
}
}
}.navigationBarTitle(Text("Available Views"))
.actionSheet(isPresented: $isPresented, content: {
ActionSheet(title: Text("Hello"))
})
}
Section {
Button(action: launchView) {
Text("Select: \(ChosenView.choices[chosenView.type])")
}
}
Spacer()
}
}
private func launchView() {
isPresented = true
}
}
What am I missing?
Why can't I repeat picker selection rather than having to reboot?
I am having difficulty assign a value to a Published variable, (weeklyAllowance).
There are 4 variables in total, of which 3 are populated outside of a button action (TextFields) working as expected
The last variable (WeeklyAllowance) must get value from with the action button.
Thanks in advance
class Users: ObservableObject {
#Published var firstName = ""
#Published var lastName = ""
#Published var age = ""
#Published var weeklyAllowance = ""
}
import SwiftUI
struct ContentView: View {
#ObservedObject var myDetails = Users()
var body: some View {
NavigationView {
VStack {
TextField("Enter Firstname",text: $myDetails.firstName)
TextField("Enter Lastname",text: $myDetails.lastName)
TextField("Enter Age",text: $myDetails.age)
Text("My name is \(myDetails.firstName)")
Text("My Last name is \(myDetails.lastName)")
Text("My age is \(myDetails.age)")
//Spacer()
VStack {
//Now Update the details
TextField("Name Update",text: $myDetails.lastName)
Text("Lastname update \(myDetails.lastName)")
}
Spacer()
//Link to new view
Button(action: {
//Assign period in the
self.myDetails.objectWillChange.send()
self.myDetails.weeklyAllowance = "Weekly"
}) {
NavigationLink(destination: detailUpdate(UpdateFirstname: $myDetails.firstName, UpdateLastname: $myDetails.lastName, UpdateAge: $myDetails.age,periodWkly: $myDetails.weeklyAllowance)) {
Text("The Link")
}
}
}.padding()
}
}
}
This is a update Test
struct detailUpdate: View {
#Binding var UpdateFirstname: String
#Binding var UpdateLastname: String
#Binding var UpdateAge: String
#Binding var periodWkly : String
var body: some View {
VStack {
Text("My First Name Update is \(UpdateFirstname)")
Text("My Last Name Update is \(UpdateLastname)")
Text("My Age Update is \(UpdateAge)")
Text("This is a \(periodWkly) allowance")
}.padding()
}
}
In your code button does not work, but works directly a link (it does not matter that you place it in button's label - on tap activated a link, not a button).
Here is your ContentView that activates link on button click:
struct ContentView: View {
#ObservedObject var myDetails = Users()
#State private var activatedLink = false
var body: some View {
NavigationView {
VStack {
TextField("Enter Firstname",text: $myDetails.firstName)
TextField("Enter Lastname",text: $myDetails.lastName)
TextField("Enter Age",text: $myDetails.age)
Text("My name is \(myDetails.firstName)")
Text("My Last name is \(myDetails.lastName)")
Text("My age is \(myDetails.age)")
//Spacer()
VStack {
//Now Update the details
TextField("Name Update",text: $myDetails.lastName)
Text("Lastname update \(myDetails.lastName)")
}
Spacer()
//Link to new view
Button(action: {
//Assign period in the
self.myDetails.weeklyAllowance = "Weekly"
self.activatedLink = true
}) {
Text("The Link")
}
NavigationLink(destination: detailUpdate(UpdateFirstname: $myDetails.firstName, UpdateLastname: $myDetails.lastName, UpdateAge: $myDetails.age, periodWkly: $myDetails.weeklyAllowance), isActive: $activatedLink) {
EmptyView()
}
}.padding()
}
}
}
You may not have to embed a navigationLink inside a button. Just use it directly.
NavigationLink(destination: detailUpdate(UpdateFirstname: $myDetails.firstName, UpdateLastname: $myDetails.lastName, UpdateAge: $myDetails.age,periodWkly: $myDetails.weeklyAllowance)) {
Text("The Link")
}.onDisappear{
self.myDetails.weeklyAllowance = "Weekly"
}