How to display a random item in a list - swift

I want when the app is launched it shows a random question in my list and I only know how to show all my questions. Please can anyone help?
This is my view
import SwiftUI
struct ViewJouer2: View {
#EnvironmentObject var data : DefisList
var body: some View {
List {
ForEach(data.defis) { Defi in
DefiRow(Defi: Defi)
}
}
}
}
struct ViewJouer2_Previews: PreviewProvider {
static var previews: some View {
ViewJouer2()
.environmentObject(DefisList())
}
}
This is my Data
import SwiftUI
class DefisList : ObservableObject {
#Published var defis = [
Defi(question: "How old are you?"),
Defi(question: "How are you"),
Defi(question: "What is your name?"),
]
}
struct Defi : Identifiable {
var question : String
var id = UUID()
}

If I understood your question correctly, you can use randomElement method to randomize the item you want to show. For example, your code can look like next:
import SwiftUI
struct ViewJouer2: View {
#EnvironmentObject var data : DefisList
var body: some View {
List {
if (!data.defis.isEmpty) {
DefiRow(Defi: data.defis.randomElement()!)
}
}
}
}
As an alternative you can use Int.random:
import SwiftUI
struct ViewJouer2: View {
#EnvironmentObject var data : DefisList
var body: some View {
List {
if (!data.defis.isEmpty) {
DefiRow(Defi: data.defis[Int.random(in: 0..<data.defis.count)])
}
}
}
}

Related

Cannot find ... in scope

I want the string in text field take the values of name of Player [0]. I try this and I have this error "Cannot find '$PlayerList' in scope".
import SwiftUI
class PlayerList : ObservableObject {
#Published var Players = [
Player(name: ""),
Player(name: ""),
]
init() {
}
}
struct Player : Identifiable {
var name : String
var id = UUID()
}
struct ViewJouer: View {
#StateObject var viewModel: PlayerList = PlayerList()
var body: some View {
VStack {
TextField("Player 1", text: $PlayerList.Players[0].name)
}
}
}
struct ViewJouer_Previews: PreviewProvider {
static var previews: some View {
ViewJouer()
}
}
You don't have the PlayerList variable in the view, you called it viewModel.
Moreover, please use the Swift notation: variables start with lowercase letters.
Here's the corrected code:
import SwiftUI
class PlayerList : ObservableObject {
// Variables start with lowercase letter
#Published var players = [
Player(name: ""),
Player(name: ""),
]
}
struct Player : Identifiable {
var name : String
// If you don't change the id, make it a constant
let id = UUID()
}
struct ViewJouer: View {
// Better make your local variables private
#StateObject private var viewModel = PlayerList()
var body: some View {
// The player list is part of the view model
VStack {
TextField("Player 1", text: $viewModel.players[0].name)
}
}
}
struct ViewJouer_Previews: PreviewProvider {
static var previews: some View {
ViewJouer()
}
}

Add a button to refresh a element in a list

I have a list of questions and when the app is launched one randomly pops up. Now I want when a button is clicked it shows another random question in the list. Any ideas?
This is my view
import SwiftUI
struct ViewJouer2: View {
#EnvironmentObject var data : DefisList
var body: some View {
List {
if let randomDefi = data.defis.randomElement() {
DefiRow(Defi: randomDefi)
}
}
}
}
struct ViewJouer2_Previews: PreviewProvider {
static var previews: some View {
ViewJouer2()
.environmentObject(DefisList())
}
}
This is my Data
import SwiftUI
class DefisList : ObservableObject {
#Published var defis = [
Defi(question: "How old are you?"),
Defi(question: "How are you"),
Defi(question: "What is your name?"),
]
}
struct Defi : Identifiable {
var question : String
var id = UUID()
}
Make randomDefi a #Published variable of your ObservedObject. Add a method called randomize() to choose a random one and call that from your button.
import SwiftUI
struct ViewJouer2: View {
#EnvironmentObject var data : DefisList
var body: some View {
List {
DefiRow(defi: data.randomDefi)
}
Button("random") {
data.randomize()
}
}
}
class DefisList : ObservableObject {
#Published var randomDefi = Defi(question: "")
private var defis = [
Defi(question: "How old are you?"),
Defi(question: "How are you?"),
Defi(question: "What is your name?"),
]
init() {
randomize()
}
func randomize() {
randomDefi = defis.randomElement()!
}
}
struct Defi : Identifiable {
var question : String
var id = UUID()
}
struct DefiRow: View {
let defi: Defi
var body: some View {
Text(defi.question)
}
}

How do I pass a value to a ViewModel from a View using the .environmentObject() in Swift?

I have created a ViewModel with an init() that accepts a parameter something like this. PS: Learning swift and swiftUI
//UsersViewModel.swift
class UsersViewModel: ObservableObject {
#Published var users: [User]
#Published var category: String
init(category: String) {
self.category = continentcategory
self.users = UserData().getUsers(byCategory: category)
}
}
UserData is the Data Model where I have a function getUsers(byCategory) that allows me to get a subset of data instead of all data and then filtering it.
For my SwiftUI view
//UserListByCategory.swift
import SwiftUI
struct UserListByCategory: View {
#EnvironmentObject var ud: UsersViewModel
var body: some View {
Text("Hello")
}
}
struct UserListByCategory_Previews: PreviewProvider {
static var previews: some View {
UserListByCategory()
.environmentObject(UsersViewModel(category: "Office"))
}
}
This above SwiftUI View gets called by another ListView after the user selects a category. How do I pass that category without hardcoding it here?
In SwiftUI we don't use view model objects for our view data. View structs are our primary encapsulation mechanism, e.g.
If you need to fetch your data:
struct UserListByCategory: View {
let category: String
#State var users: [User] = []
var body: some View {
List {
ForEach(users) { user in
Text("\(user.name)")
}
}
.task(id: category) {
users = await fetchUsers(category: category)
}
}
}
struct UserListByCategory_Previews: PreviewProvider {
static var previews: some View {
UserListByCategory(category: "Office")
}
}
If you already have all the model data, pass it down the View struct hierarchy as follows:
struct ContentView: View {
#ObservedObject var model: Model
var body: some View {
UserList(users: model.users(category:"Office"))
}
}
struct UserList: View {
let users: [User]
var body: some View {
List {
ForEach(users) { user in
Text("\(user.name)")
}
}
}
}

SwiftUI Sharing model data between views

I have such a structure, this structure seems wrong to me as the approach I want to ask you. So when I want to use 2 models in 1 view, I have to put it in foreach in one view. This is what I want. Using the data I use in my user's profile on other pages I want. How should I do this? How do you guys do it?
Let me give an example for your better understanding:
I want to show my user's Username data on the Homepage, how should I do this?. In fact, after initializing my model once, I want to use it in other views. What is the right approach.
import SwiftUI
struct ContentView: View {
#StateObject var network = ProfileNetwork()
var body: some View {
TabView{
ProfileView().tabItem { Image(systemName: "house") }
ForEach(self.network.userprofile,id:\.id){a in
ShopView(profile_model: a)
}.tabItem { Image(systemName: "house") }
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
class ProfileNetwork : ObservableObject {
#Published var userprofile : [UserPRofile] = [UserPRofile(name: "Test", coin: 1, id: "dsa")]
}
struct ProfileView : View {
#StateObject var network = ProfileNetwork()
var body: some View {
ForEach(self.network.userprofile, id:\.id){ i in
ProfileViewModel(profile_model: i)
}
}
}
struct ProfileViewModel : View {
var profile_model : UserPRofile
var body: some View {
Text(self.profile_model.name)
}
}
struct UserPRofile : Decodable{
var name : String
var coin : Int
var id : String
}
class ShopeNetwork : ObservableObject {
#Published var shop : [ShopStore] = [ShopStore(id: "sda", image: "dasd", price: 100, name: "sda")]
}
struct ShopView : View {
#StateObject var network = ShopeNetwork()
var profile_model : UserPRofile
var body: some View {
ForEach(self.network.shop, id:\.id){ c in
ShopViewModel(shop_model: c, profile_model: profile_model)
}
}
}
struct ShopViewModel : View {
var shop_model : ShopStore
var profile_model : UserPRofile
var body: some View {
Text(profile_model.name)
Text(self.shop_model.name)
}
}
struct ShopStore : Decodable {
var id : String
var image : String
var price : Int
var name : String
}
A possible solution is to create an #EnvironmentObject and inject it at the root level:
class AppState: ObservableObject {
#Published var userProfile: UserPRofile?
}
#main
struct TestApp: App {
#StateObject private var appState = AppState()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(appState)
}
}
}
struct ProfileView: View {
#EnvironmentObject private var appState: AppState // access as an `#EnvironmentObject`
#StateObject var network = ProfileNetwork()
var body: some View {
VStack {
ForEach(self.network.userprofile, id: \.id) { i in
ProfileViewModel(profile_model: i)
}
}
.onAppear {
appState.userProfile = network.userprofile.first // set `userProfile` globally
}
}
}
struct ShopView: View {
#EnvironmentObject private var appState: AppState // use it in any other view
...
Swift 5, iOS 14
Make the class a singleton so it becomes shareable easily.
class ProfileNetwork : ObservableObject {
#Published var userprofile : [UserPRofile] = [UserPRofile(name: "Test", coin: 1, id: "dsa")]
static var shared = ProfileNetwork()
}
And then refer to it with the shared handle.
struct ContentView: View {
#StateObject var network = ProfileNetwork.shared
var body: some View {
....
}
struct ProfileView : View {
#StateObject var network = ProfileNetwork.shared
var body: some View {
....
}

How to use Bind an Associative Swift enum?

I have a GroupView that accepts a binding as a parameter because I want the GroupView to modify the data in the enum.
Can some help me on how to accomplish this?
import SwiftUI
struct ContentView: View {
#ObservedObject var viewModel = ViewModel()
var body: some View {
VStack {
GroupView(group: /* What do i put here? */) // <----------------
}
}
}
struct GroupView: View {
#Binding var group: Group
var body: some View {
Text("Hello World")
}
}
class ViewModel : ObservableObject {
#Published var instruction: Instruction!
init() {
instruction = .group(Group(groupTitle: "A Group struct"))
}
}
enum Instruction {
case group(Group)
}
struct Group { var groupTitle: String }
Well, this certainly will work... but probably there's a better approach to your problem. But no one is in a better position than you, to determine that. So I'll just answer your question about how to pass a binding.
struct ContentView: View {
#ObservedObject var viewModel = ViewModel()
var body: some View {
VStack {
GroupView(group: viewModel.groupBinding)
}
}
}
class ViewModel : ObservableObject {
#Published var instruction: Instruction!
init() {
instruction = .group(Group(groupTitle: "A Group struct"))
}
var groupBinding: Binding<Group> {
return Binding<Group>(get: {
if case .group(let g) = self.instruction {
return g
} else {
return Group(groupTitle: "")
}
}, set: {
self.instruction = .group($0)
})
}
}