QML - how can I get comboBox.currentText from another component? - combobox

I just started learning qml and have a question on how to get the comboBox.currentText from another component.
code example:
App {
id: app
width: px(250); height: px(250)
NavigationStack {
Page {
id: page
navigationBarHidden: true
AppText { text: "startpage" }
SimpleButton{
x: 220; y: 0; onClicked: page.navigationStack.push(settingsPage)
}
AppText {
x: 0; y: 50; text: "text " + comboBox1.currentText
}
}
}
Component {
id: settingsPage
Page {
navigationBarHidden: true
AppText { text: qsTr("settings page") }
SimpleButton{
x: 220; y: 0; onClicked: page.navigationStack.push(lastPage)
}
ComboBox {
id: comboBox1
currentIndex: 0
x: 10; y: 40
style: ComboBoxStyle {
label: Text {
text: control.currentText
}
}
model: ListModel {
ListElement { text: "green" }
ListElement { text: "dark-green" }
ListElement { text: "blue" }
}
}
AppText {
x: 0; y: 90; text: "text " + comboBox1.currentText
}
}
}
Component {
id: lastPage
Page {
navigationBarHidden: true
AppText { text: qsTr("last page") }
SimpleButton{
x: 220; y: 0; onClicked: page.navigationStack.push(page)
}
AppText {
x: px(50); y: px(90); text: "text " + comboBox1.currentText
}
}
}
}
-> I need to get the selected Listelement from the Combobox in settingspage and use it in the component with id: lastPage
Any help would be greatly appreciated

Actually I have no clue what are elements you are using here (App, NavigationStack etc.) but in general the struct can be like the following:
Window {
visible: true
width: 400
height: 300
id: root
property color someColor: "green"
Row {
id: element1
anchors.centerIn: parent
Rectangle {
width: 100
height: 100
color: root.someColor
}
Rectangle {
id: element2
width: 100
height: 100
color: "yellow"
MouseArea {
anchors.fill: parent
onClicked: {
root.someColor = "blue"
}
}
}
}
}
The element1 depends on some property from its parent/top item that always in the scope. This construction called property bindings in QML and can be done in several way (for example using Binding, good for dynamic elements ). The element2 also can access/change the property and so changing it from element2 immediately affects element1. That also solves an issue when you want to access element1 from the root and element1 not in scope, or not exists or whatever else that will crash you application in this case.

Related

SwiftUI: bound string into array of strings

I have a variable "textEditorText" bound to the "TextEditor".
When the button "send to the player" is clicked, the entire content of the text editor is transferred to the "stringArray" array.
But in this array, all the text is contained as one element, and I need to split it into words.
For example, the sentence "one, two, three" needs to be turned into the elements "one", "two", "three
I'm trying to do this by passing the content from stringArray to stringArray2 but I'm getting the error messages
How can I overcome this situation?
Thank you.
import SwiftUI
struct ContentView: View {
#State var textEditorText:String = "one two three"
#State var stringArray:[String] = []
var stringArray2:[String] = stringArray.components(separatedBy: " ")
var body: some View {
NavigationView {
VStack{
TextEditor(text: $textEditorText)//binding to #State var textEditorText
.frame(height: 200)
.cornerRadius(10)
Button (action: {
textEditorText = ""
}, label: {
Text("clear the editor".uppercased())
.font(.headline)
//.foregroundColor(.white)
.padding(10)
.frame(maxWidth: .infinity)
.foregroundColor(.white)
.background(Color.blue)
.cornerRadius(10)
})
Button (action: {
if stringArray.isEmpty && !textEditorText.isEmpty {
stringArray.append(textEditorText)
}
}, label: {
Text("send to the player".uppercased())
.font(.headline)
.padding(10)
.frame(maxWidth: .infinity)
.foregroundColor(.white)
//.background(stringArray.isEmpty ? Color.red : Color.blue)
.background(Color.blue)
.cornerRadius(10)
})
Button(action: {
stringArray.removeAll()
}, label: {
Text("clear the player".uppercased())
.padding(10)
.frame(maxWidth: .infinity)
.background(Color.blue.cornerRadius(10))
.foregroundColor(.white)
.font(.headline)
.shadow(radius: 5)
})
ForEach(stringArray, id: \.self) { data in
Text(data)
}
.frame(maxWidth:.infinity, maxHeight: 30)
Spacer()
} //VStack
.padding(10)
.background(Color.gray.opacity(0.25))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
There's a lot of unnecessary code in your question, but if I understand correctly what you're trying to do, you just need to change it to use
stringArray.append(contentsOf: )
rather than
stringArray.append()
for example:
if stringArray.isEmpty && !textEditorText.isEmpty {
stringArray.append(contentsOf: textEditorText.components(separatedBy: " "))
}

Find an item and change value in custom object array - SwifUI

I have this specific data that I want to use for my app.
struct AssetData : Identifiable {
var id: Int
var title: String
var image: Image
var price: Int
var pricePerSec: Int
var own: Bool
}
class AssetDatas : ObservableObject {
#Published var assetData: [AssetData] = []
init() {
getAssetData()
}
func getAssetData() {
let asset1 = AssetData(id: 0, title: "株",image: Image("asset1"), price: 1000, pricePerSec: 10000, own: false)
let asset2 = AssetData(id: 1, title: "時計",image: Image("asset2"), price: 2000, pricePerSec: 20000, own: false)
let asset3 = AssetData(id: 2, title: "車",image: Image("asset3"), price: 3000 , pricePerSec: 50000, own: false)
let asset4 = AssetData(id: 3, title: "家",image: Image("asset4"), price: 4000, pricePerSec: 50000, own: false)
let asset5 = AssetData(id: 4, title: "ダイヤモンド",image: Image("asset5"), price: 5000, pricePerSec: 50000, own: false)
let asset6 = AssetData(id: 5, title: "船",image: Image("asset6"), price: 6000, pricePerSec: 50000, own: false)
let asset7 = AssetData(id: 6, title: "飛行機",image: Image("asset7"), price: 7000, pricePerSec: 50000, own: false)
let asset8 = AssetData(id: 7, title: "人工衛星",image: Image("asset8"), price: 8000, pricePerSec: 50000, own: false)
let asset9 = AssetData(id: 8, title: "月",image: Image("asset9"), price: 9000, pricePerSec: 50000, own: false)
let asset10 = AssetData(id: 9, title: "宇宙人",image: Image("asset10"), price: 10000, pricePerSec: 50000, own: false)
self.assetData.append(contentsOf: [
asset1,
asset2,
asset3,
asset4,
asset5,
asset6,
asset7,
asset8,
asset9,
asset10
])
}
}
I am looking for a way to change a value of an item in the array such as "price" or "own" when the user clicks a button in a view. Is this possible?
my view is something like this
struct AssetListView : View {
var level = 1
#EnvironmentObject var user : UserData
#StateObject var asset = AssetDatas()
#State private var buttonBackColor:Color = .yellow
#State private var buttonText: String = "買う"
let timer = Timer.publish(every: 1, on: .current, in: .common).autoconnect()
var body: some View {
ScrollView{
VStack(alignment: .leading){
ForEach(asset.assetData) { assets in
HStack{
assets.image
.resizable()
.frame(width:50, height: 50)
Text(assets.title)
.font(.headline)
Spacer()
Text("金額: \(assets.price)円")
Button(action: {
if user.pocketMoney >= assets.price && buttonText == "買う"{
user.pocketMoney -= assets.price
self.buttonText += "売る"
self.buttonBackColor = .blue
};if buttonText == "売る"{
buttonText = "買う"
self.buttonBackColor = .blue
user.pocketMoney += assets.price
} else {
}
}) {
Text(buttonText)
.padding(10)
.background(buttonBackColor)
.cornerRadius(15)
}
}
}
.foregroundColor(.white)
.padding()
.background(Color("Color3").cornerRadius(10).opacity(0.8))
.padding(.horizontal)
}
}
}
}
In the view, I have a button, and when I click the button, I want the price of the button to change, and I also want to change the bool of "own". How can I do that?
In iOS 15+, you can use a simple syntax to get a binding to the element in your ForEach that you wish to modify:
struct ContentView : View {
#StateObject var assets = AssetDatas()
var body: some View {
ForEach($assets.assetData) { $asset in
HStack {
Text("\(asset.price)")
Button("Change price") {
asset.price = 0
}
}
}
}
}
Prior to the introduction of that feature, you could provide a custom binding:
//Inside AssetDatas
func bindingForAsset(id: Int) -> Binding<AssetData> {
.init {
self.assetData.first(where: { $0.id == id })!
} set: { newValue in
self.assetData = self.assetData.map { $0.id == id ? newValue : $0 }
}
}
struct ContentView : View {
#StateObject var assets = AssetDatas()
var body: some View {
ForEach(assets.assetData) { asset in
HStack {
Text("\(asset.price)")
Button("Change price") {
assets.bindingForAsset(id: asset.id).wrappedValue.price = 0
}
}
}
}
}
You can use a ForEach element binding. See here for more information on what that is. In simple terms, it allows you to pass in a Binding into your ForEach, and then you can get a Binding for each element.
In this example, you give $asset.assetData (Binding<[AssetData]>) and get $asset (Binding<AssetData>), asset (AssetData), and _asset (not needed).
The three changes are marked with <- HERE. Code:
var body: some View {
ScrollView{
VStack(alignment: .leading){
ForEach($asset.assetData) { $assets in // <- HERE
HStack{
assets.image
.resizable()
.frame(width:50, height: 50)
Text(assets.title)
.font(.headline)
Spacer()
Text("金額: \(assets.price)円")
Button(action: {
assets.price = 10 // <- HERE
assets.own = true // <- HERE
if user.pocketMoney >= assets.price && buttonText == "買う"{
user.pocketMoney -= assets.price
self.buttonText += "売る"
self.buttonBackColor = .blue
};if buttonText == "売る"{
buttonText = "買う"
self.buttonBackColor = .blue
user.pocketMoney += assets.price
} else {
}
}) {
Text(buttonText)
.padding(10)
.background(buttonBackColor)
.cornerRadius(15)
}
}
}
.foregroundColor(.white)
.padding()
.background(Color("Color3").cornerRadius(10).opacity(0.8))
.padding(.horizontal)
}
}
}

Change button/label text using key:value from local JSON - SwiftUI

New to SwiftUI. Trying to get a JSON key:value array to update to the next random item when the user presses the button. Got it to load up just fine, but the button does nothing. Tried making a shuffle function, but couldn't find a way to pass in the new values to the Text areas. Also tried to make my decodedQuotes and quote variables into #State vars inside the View, but they initialize before self is available.
Could normally call touchesBegan and write a simple function in Storyboard. Is there something similar I could do here?
var decodedQuotes = Bundle.main.decode([Quote].self, from: "quotes.json")
// parses an array with "quote":"name" pairs
var quote = decodedQuotes.randomElement()!
struct QuoteView: View {
var body: some View {
Button(action:
// Need it to update the Text below with a new random item from quote
)
HStack {
VStack {
HStack(alignment: .center) {
Text(quote.quote)
.multilineTextAlignment(.center)
.padding()
.foregroundColor(.black)
}
HStack {
Text("-\(quote.name)")
.foregroundColor(.black)
}
}
}
.frame(width: 300, height: 300, alignment: .center)
.background(Background(isHighlighted: true, shape: Rectangle()))
.foregroundColor(.blue)
.padding(4)
.cornerRadius(20)
}
}
You were on the right track with #State
struct Quote {
var quote : String
var name : String
}
var decodedQuotes = [Quote(quote: "test1", name: "name1"),
Quote(quote: "test2", name: "name2"),
Quote(quote: "test3", name: "name3"),]
struct QuoteView: View {
#State var quote : Quote? = decodedQuotes.randomElement()
var body: some View {
Button(action: {
quote = decodedQuotes.randomElement()
}) {
Text("New quote")
}
if let quote = quote {
HStack {
VStack {
HStack(alignment: .center) {
Text(quote.name)
.multilineTextAlignment(.center)
.padding()
.foregroundColor(.black)
}
HStack {
Text("-\(quote.name)")
.foregroundColor(.black)
}
}
}
.frame(width: 300, height: 300, alignment: .center)
.foregroundColor(.blue)
.padding(4)
.cornerRadius(20)
}
}
}
Obviously, for testing, I just used an array of pre-made Quotes
If you wanted to, you could make decodedQuotes a #State property on the QuoteView as well and decode them in onAppear
I've also chosen to make quote an optional for now. I check to see if it's available by doing the if let quote = quote line. This should be a bit future-proof in case you start loading quotes from other places at some point.
I believe this is a better implementation in the current SwiftUI where the text actually changes within the button. I hope it helps>
import SwiftUI
struct Quote {
var quote : String
var name : String
}
var decodedQuotes = [Quote(quote: "Title 1", name: "Description 1."),
Quote(quote: "Title 2", name: "Second description."),
Quote(quote: "Title 3", name: "final item."),]
struct ContentView: View {
#State var quote : Quote? = decodedQuotes.randomElement()
var body: some View {
Button(action: {
quote = decodedQuotes.randomElement()
}) {
Text("New quote")
if let quote = quote {
HStack {
VStack {
VStack(alignment: .center) {
Text(quote.quote)
.multilineTextAlignment(.center)
.padding()
.foregroundColor(.blue)
Text("-\(quote.name)")
.foregroundColor(.blue)
}
}
}
.frame(width: 300, height: 300, alignment: .center)
.foregroundColor(.blue)
.padding(4)
.cornerRadius(20)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

How would I condense these separate labels to an array of labels in my custom UICollectionView cell?

This code works perfectly fine, but I am assuming there is a much more efficient way of doing things like this. I am providing the first four items of an array of strings (this list can contain an unlimited amount of variables) to the user as a "snapshot" view. The code below is inside of a custom cell class.
var tray: Tray? {
didSet {
guard let trayTitle = tray?.title else { return }
guard let trayItems = tray?.items else { return }
// tray.items will give an array of strings
trayTitleLabel.text = trayTitle
if trayItems.count == 1 {
firstSnapshotLabel.text = trayItems[0]
secondSnapshotLabel.text = ""
thirdSnapshotLabel.text = ""
fourthSnapshotLabel.text = ""
} else if trayItems.count == 2 {
firstSnapshotLabel.text = trayItems[0]
secondSnapshotLabel.text = trayItems[1]
thirdSnapshotLabel.text = ""
fourthSnapshotLabel.text = ""
} else if trayItems.count == 3 {
firstSnapshotLabel.text = trayItems[0]
secondSnapshotLabel.text = trayItems[1]
thirdSnapshotLabel.text = trayItems[2]
fourthSnapshotLabel.text = ""
} else if trayItems.count >= 4 {
firstSnapshotLabel.text = trayItems[0]
secondSnapshotLabel.text = trayItems[1]
thirdSnapshotLabel.text = trayItems[2]
fourthSnapshotLabel.text = trayItems[3]
} else {
firstSnapshotLabel.text = ""
secondSnapshotLabel.text = ""
thirdSnapshotLabel.text = ""
fourthSnapshotLabel.text = ""
}
I then manually set up each label:
let firstSnapshotLabel: UILabel = {
let label = UILabel()
label.attributes(text:"Item 1", textColor: Colors.appDarkGrey, alignment: .left, font: Fonts.rubikRegular, size: 12, characterSpacing: 0.5, backgroundColor: nil)
return label
}()
let secondSnapshotLabel: UILabel = {
let label = UILabel()
label.attributes(text:"Item 2", textColor: Colors.appDarkGrey, alignment: .left, font: Fonts.rubikRegular, size: 12, characterSpacing: 0.5, backgroundColor: nil)
return label
}()
let thirdSnapshotLabel: UILabel = {
let label = UILabel()
label.attributes(text:"Item 3", textColor: Colors.appDarkGrey, alignment: .left, font: Fonts.rubikRegular, size: 12, characterSpacing: 0.5, backgroundColor: nil)
return label
}()
let fourthSnapshotLabel: UILabel = {
let label = UILabel()
label.attributes(text:"Item 4", textColor: Colors.appDarkGrey, alignment: .left, font: Fonts.rubikRegular, size: 12, characterSpacing: 0.5, backgroundColor: nil)
return label
}()
As I said, the code above gives me what I wanted, but it is extremely messy and inefficient.
Here is something else I've tried that didn't quite work. I am assuming that the "snapshotLabels" were loaded into the view before the "snapshots" array was populated. I assume this because when I print the "snapshots" I get the populated array, but when I print the "snapshotLabels" I get an empty array.
var tray: Tray? {
didSet {
guard let trayTitle = tray?.title else { return }
guard let trayItems = tray?.items else { return }
// tray.items will give an array of strings
trayTitleLabel.text = trayTitle
for item in trayItems {
snapshots.append(item)
}
}
}
var snapshots = [] as [String]
lazy var snapshotLabels = snapshots.prefix(4).map { item -> UILabel in
let label = UILabel()
label.attributes(text:"\(item)", textColor: Colors.appDarkGrey, alignment: .left, font: Fonts.rubikRegular, size: 12, characterSpacing: 0.5, backgroundColor: nil)
return label
}
Tray struct and array of instances:
struct Tray {
let title: String
let items: [String]
}
let trays = [Tray(title: "Groceries", items: ["Apples","Water","Milk","Eggs","Fruity Pebbles","Bread","Butter","Flour","Yogurt","Pop-Tarts"]),
Tray(title: "Home", items: ["Laundry","Trash","Clean desk"]),
Tray(title: "Miscellaneous", items: ["Call mom","Feed dogs"]),
Tray(title: "Bucket List", items: ["Skydiving","England","Pilot's license","Learn spanish","Barcelona","Italy"]),
Tray(title: "Dinner", items: ["Pasta","Crabs","Tacos","Pizza","Steak","Cheesesteaks","Chicken and Rice"])
]
Does anyone know why this is happening?
Thanks! :D
Make your labels an array
let labels = (0..<4).map {
let label = UILabel()
label.attributes(text:"Item \($0)", textColor: Colors.appDarkGrey, alignment: .left, font: Fonts.rubikRegular, size: 12, characterSpacing: 0.5, backgroundColor: nil)
return label
}
then iterate over your tray items to set the value on the corresponding label
(0..<trayItems.count).forEach { labels[$0].text = trayItems[$0] }
(trayItems.count..<labels.count).forEach { $0.text = "" }

on TabGesture Swift UI

look for some help on my study of the scrollView in swiftUI.
I have a scroll view that display the value of an array, base when the user tap on the different item of scroll view I want to display it on the textField below.
how can I pass the array value to the text field??
import SwiftUI
struct ContentView: View {
let post = ["TEST1 ","Test 2" , "Test 3","TEST4 ","Test 5" , "Test 6"]
var temp = ""
var body: some View {
VStack {
ScrollView(.horizontal, content: {
HStack(spacing: 100) {
ForEach(post, id: \.self){ item in
ZStack {
Rectangle().foregroundColor(.blue).frame(width: 190, height: 170, alignment: .center)
Text(item)
}.onTapGesture {
// pass the value off Scroll View to the text
debugPrint("\(item)")
}
}
}
.padding(.leading, 10)
})
.frame(height: 190)
Spacer()
Text("dispaly here array value selected")
Spacer()
}
}
}
thank for helping me...
The trick here is you need to #State temp when you need to assign to a #State value inside the view.
struct ContentView: View {
let post = ["TEST1 ","Test 2" , "Test 3","TEST4 ","Test 5" , "Test 6"]
#State private var temp = ""
var body: some View {
VStack {
ScrollView(.horizontal, content: {
HStack(spacing: 100) {
ForEach(post, id: \.self){ item in
ZStack {
Rectangle().foregroundColor(.blue).frame(width: 190, height: 170, alignment: .center)
Text(item)
}.onTapGesture {
// pass the value off Scroll View to the text
self.temp = item
}
}
}
.padding(.leading, 10)
})
.frame(height: 190)
Spacer()
Text( self.temp)
Spacer()
}
}
}

Resources