Every time I press the button an instance of the School struct is created. I want to show that change on the screen but the button doesn't update. What am I missing to make it work?
import SwiftUI
struct School {
static var studentCount = 0
static func add(student: String) {
print("\(student) joined the school.")
studentCount += 1
}
}
struct ReviewClosuresAndStructs: View {
#State private var test = School.studentCount
var body: some View {
VStack {
Button(action: {
School.add(student: "Tyler")
}, label: {
Text("Press me. \n Students: \(test)")
})
}
}
}
struct ReviewClosuresAndStructs_Preview: PreviewProvider {
static var previews: some View {
ReviewClosuresAndStructs()
}
}
struct School {
var studentCount = 0 // <= here
mutating func add(student: String) { // <= here
print("\(student) joined the school.")
studentCount += 1
}
}
struct ReviewClosuresAndStructs: View {
#State private var test = School() // <= here
var body: some View {
VStack {
Button(action: {
test.add(student: "Tyler")
}, label: {
Text("Press me. \n Students: \(test.studentCount)") // <= here
})
}
}
}
Related
I'm trying to change views after an array goes unchanged for one iteration cycle. However, it takes one extra button press to change views.
import SwiftUI
struct ContentView: View {
#State private var numbers: [String] = ["4", "1", "2", "5", "3"]
#State private var numbersCheck: [String] = []
#State private var index: Int = 0
#State private var done: Bool = false
var body: some View {
if done {
DoneView()
} else {
HStack {
Button(action: {
click(swap: false)
}) {
Text(numbers[index])
}
Button(action: {
click(swap: true)
}) {
Text(numbers[index + 1])
}
}
}
}
func click(swap: Bool) {
if index == 0 {
numbersCheck = numbers
}
if swap {
numbers.swapAt(index, index + 1)
}
if index < numbers.count - 2 {
index += 1
} else {
if numbersCheck != numbers {
index = 0
} else {
done = true
}
}
}
}
struct DoneView: View {
var body: some View {
Text("Done!")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I've tried different approaches to changing views (e.g. different structs, one struct/different view bodies, binding variables, etc.), but nothing's working.
import Foundation
class Card: Identifiable, ObservableObject {
var id = UUID()
var result: String = ""
init() {
for _ in 1...10 {
let suit = ["D","K","B","S"][Int(arc4random() % 4]
let numStr = ["1","2","3","4"][Int(arc4random() % 4]
result = suit + numStr
}
}
}
import SwiftUI
struct EasyModeView: View {
#ObservedObject var cardModel: Card
var body : some View {
HStack {
ForEach((1...10, id: \.self) { _ in
Button { } label: { Image(cardModel.result) } } }
}
}
I created loop but it always shows me 10 same cards and i want all 10 different.
My Images in Assets are named same as combination of two variables example "D1","D2","S1","S2" etc.
Here is a right way of what you are trying:
struct ContentView: View {
var body: some View {
EasyModeView()
}
}
struct EasyModeView: View {
#StateObject var cardModel: Card = Card()
var body : some View {
HStack {
ForEach(cardModel.results) { card in
Button(action: {}, label: {
Text(card.result)
.padding(3.0)
.background(Color.yellow.cornerRadius(3.0))
})
}
}
Button("re-set") { cardModel.reset() }.padding()
}
}
struct CardType: Identifiable {
let id: UUID = UUID()
var result: String
}
class Card: ObservableObject {
#Published var results: [CardType] = [CardType]()
private let suit: [String] = ["D","K","B","S"]
init() { initializing() }
private func initializing() {
if (results.count > 0) { results.removeAll() }
for _ in 0...9 { results.append(CardType(result: suit.randomElement()! + String(describing: Int.random(in: 1...4)))) }
}
func reset() { initializing() }
}
Result:
You are using only one card, instead of creating 10. Fix like this
struct EasyModeView: View {
var body : some View {
HStack {
ForEach(0..<10) { _ in
Button { } label: {
Image(Card().result)
}
}
}
}
}
I am trying to run a function after a user has selected a picker option.
The idea is that the user can set a default station, so I need to be able to send the selected value to a function so I can save it inside the core data module. How can I achieve this?
import SwiftUI
struct SettingsView: View {
var frameworks = ["DRN1", "DRN1 Hits", "DRN1 United", "DRN1 Life"]
#State private var selectedFrameworkIndex = 0
func doSomethingWith(value: String) {
print(value)
}
var body: some View {
NavigationView {
Form {
Section {
Picker(selection: $selectedFrameworkIndex, label: Text("Favorite Station")) {
ForEach(0 ..< frameworks.count) {
Text(self.frameworks[$0])
}
}.onReceive([self.frameworks].publisher.first()) { value in
self.doSomethingWith(value: value)
}
}
}
.navigationBarTitle("Settings")
}
}
}
Instead of using onRecive try using onChange.
Your code:
.onReceive([self.frameworks].publisher.first()) { value in
self.doSomethingWith(value: value)
}
Correction:
.onChange(of: $selectedFrameworkIndex, perform: { value in
self.doSomethingWith(value: value)
})
this will trigger every time $selectedFrameworkIndex is changed.
I would use a simple .onChange(of:) call on the picker. It will pick up on the change of the #State var and allow you to act on it. Use it like this:
import SwiftUI
struct SettingsView: View {
var frameworks = ["DRN1", "DRN1 Hits", "DRN1 United", "DRN1 Life"]
#State private var selectedFrameworkIndex = 0
func doSomethingWith(value: String) {
print(value)
}
var body: some View {
NavigationView {
Form {
Section {
Picker(selection: $selectedFrameworkIndex, label: Text("Favorite Station")) {
ForEach(0 ..< frameworks.count) {
Text(self.frameworks[$0])
}
}
// Put your function call in here:
.onChange(of: selectedFrameworkIndex) { value in
print(value)
}
}
}
}
.navigationBarTitle("Settings")
}
}
}
yours:
import SwiftUI
struct SettingsView: View {
var frameworks = ["DRN1", "DRN1 Hits", "DRN1 United", "DRN1 Life"]
#State private var selectedFrameworkIndex = 0
func doSomethingWith(value: String) {
print(value)
}
var body: some View {
NavigationView {
Form {
Section {
Picker(selection: $selectedFrameworkIndex, label: Text("Favorite Station")) {
ForEach(0 ..< frameworks.count) {
Text(self.frameworks[$0])
}
}.onReceive([self.frameworks].publisher.first()) { value in
self.doSomethingWith(value: value)
}
}
}
.navigationBarTitle("Settings")
}
}
}
add a variable that checks to see if selectedFrameworkIndex has changed.
change:
import SwiftUI
struct SettingsView: View {
var frameworks = ["DRN1", "DRN1 Hits", "DRN1 United", "DRN1 Life"]
#State private var selectedFrameworkIndex = 0
#State private var savedFrameworkIndex = 0
func handler<Value: Any>(val: Value) -> Value {
if selectedFrameworkIndex != savedFrameworkIndex {
self.doSomethingWith(value: self.frameworks[selectedFrameworkIndex])
}
return val
}
func doSomethingWith(value: String) {
print(value)
}
var body: some View {
NavigationView {
Form {
Section {
Picker(selection: handler(val: $selectedFrameworkIndex), label: Text("Favorite Station")) {
ForEach(0 ..< frameworks.count) {
Text(self.frameworks[$0])
}
}
}
}
.navigationBarTitle("Settings")
}
}
}
I need to pass a parameter calledFrom to a Sheet in SwiftUI.
Strangely, the parameter is not used on the first call, but it works on the following ones.
import SwiftUI
struct ContentView: View {
#State var showSheet = false
#State var calledFrom = -1
var body: some View {
ForEach((1...4), id: \.self) { i in
getButton(i)
}
.sheet(isPresented: $showSheet) { Dialog(calledFrom: calledFrom) }
.padding()
}
func getButton(_ i : Int) -> some View {
return Button("\(i)"){print("Button \(i) pressed"); calledFrom = i; showSheet = true }
}
}
struct Dialog: View {
var calledFrom : Int
#Environment(\.presentationMode) private var presentationMode
var body: some View {
VStack{
Text("Called from Button \(calledFrom)")
Button("close"){presentationMode.wrappedValue.dismiss()}
}
.padding()
}
}
You have to use sheet(item:) to get the behavior you're looking for. In iOS 14, the sheet view is calculated before the #State changes:
struct ActiveItem : Identifiable {
var calledFrom: Int
var id: Int { return calledFrom }
}
struct ContentView: View {
#State var activeItem : ActiveItem?
var body: some View {
ForEach((1...4), id: \.self) { i in
getButton(i)
}
.sheet(item: $activeItem) { item in
Dialog(calledFrom: item.calledFrom)
}
.padding()
}
func getButton(_ i : Int) -> some View {
return Button("\(i)"){
print("Button \(i) pressed");
activeItem = ActiveItem(calledFrom: i)
}
}
}
I have this code :
import SwiftUI
struct PlayButton: View {
#Binding var isClicked: Bool
var body: some View {
Button(action: {
self.isClicked.toggle()
}) {
Image(systemName: isClicked ? "checkmark.circle.fill" : "circle")
}
}
}
struct ContentView: View {
#State private var isPlaying: Bool = false
var players : [String] = ["Crown" , "King" , "Queen" , "Prince"]
var body: some View {
VStack {
ForEach(players, id: \.self) { player in
HStack {
Text(player)
PlayButton(isClicked: $isPlaying)
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I want to deselect all other previously selected buttons if i select a new one. For example , if i select King and select queen , then King is deselected. How can i do that
What i have done. I honestly could not come with a solution .
I understand this might look like a lot more code to provide the answer but my assumption is you are trying to make a real world app. A real world app should be testable and so my answer is coming from a place where you can test your logic separate from your UI. This solution allows you to use the data to drive what your view is doing from a model perspective.
import SwiftUI
class PlayerModel {
let name: String
var isSelected : Bool = false
init(_ name: String){
self.name = name
}
}
class AppModel: ObservableObject {
let players : [PlayerModel] = [PlayerModel("Crown") , PlayerModel("King") ,PlayerModel("Queen") ,PlayerModel("Prince")]
var activePlayerIndex: Int?
init(){
}
func selectPlayer(_ player: PlayerModel){
players.forEach{
$0.isSelected = false
}
player.isSelected = true
objectWillChange.send()
}
}
struct PlayButton: View {
let isSelected: Bool
let action : ()->Void
var body: some View {
Button(action: {
self.action()
}) {
Image(systemName: isSelected ? "checkmark.circle.fill" : "circle")
}
}
}
struct ContentView: View {
#ObservedObject var model = AppModel()
var body: some View {
VStack {
ForEach(model.players, id: \.name) { player in
HStack {
Text(player.name)
PlayButton(isSelected: player.isSelected, action: { self.model.selectPlayer(player) })
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
PlayerView()
}
}
For a single selection, at a time you can pass selectedData to PlayButton view
struct PlayButton: View {
#Binding var selectedData: String
var data: String
var body: some View {
Button(action: {
selectedData = data
}) {
Image(systemName: data == selectedData ? "checkmark.circle.fill" : "circle")
}
}
}
struct ContentView: View {
#State private var selectedPlayer: String = ""
private var players : [String] = ["Crown" , "King" , "Queen" , "Prince"]
var body: some View {
VStack {
ForEach(players.indices) { index in
let obj = players[index]
HStack {
Text(obj)
PlayButton(selectedData: $selectedPlayer, data: obj)
}
}
}
}
}