What is the best way to go about resetting state variables, using a button. I've tried a load of different funcs but none work.
I'm trying to use this button:
primaryButton: .destructive(Text("Delete")) {
Code
},secondaryButton:
.cancel()
To reset these State variables:
#State var statsValue1 = 0
#State var statsValue2 = 0
#State var statsValue3 = 0
#State var statsValue4 = 0
#State var statsValue5 = 0
#State var statsValue6 = 0
(which are in the main content view)
How about using a view model, the #Published property wrapper notifies about any changes of the model and the reset function creates a new instance
struct Model {
var value1 = 0
var value2 = 0
var value3 = 0
}
class ViewModel : ObservableObject {
#Published var model = Model()
func reset() {
model = Model()
}
}
and a simple test logic in the content view
struct ContentView : View {
#StateObject var viewModel = ViewModel()
var body : some View {
VStack(spacing: 20) {
Text("Value 1: \(viewModel.model.value1)")
Text("Value 2: \(viewModel.model.value2)")
Text("Value 3: \(viewModel.model.value3)")
Divider()
Button ( "Delete", role: .destructive, action: viewModel.reset )
Button { viewModel.model.value1 += 1 } label: { Text("Increment value 1") }
Button { viewModel.model.value2 += 1 } label: { Text("Increment value 2") }
Button { viewModel.model.value3 += 1 } label: { Text("Increment value 3") }
}
}
}
Related
A similar question was asked before, and with the help of several netizens, some codes have been changed.
When press .. "Show Next View" >> "Change-Me" >> "Return" .. why the variable "price" can not change to 9 ....
I am a novice, I know there code have something wrong I can't find it and hope someone can guide me, Thanks!
class ExtenalClass {
let SubFrutis = ContentView()
func ExtChangePrice() {
SubFrutis.fChangePrice()
}
}
struct ContentView: View {
#State var price: Int = 0
#State var vPresent = false
func fChangePrice() {
price = 9
print("Price Value is: \(String(price))")
}
var body: some View {
VStack {
Text(String(price)).padding()
Button("Show Next View") { self.vPresent = true }
.sheet(isPresented: $vPresent) {
SecondView(vPresent: self.$vPresent)
}.padding()
Button("Add-Me") { price += 1 }.padding()
Button("RESET") { price = 1 }.padding()
}
}
}
struct SecondView: View {
#Binding var vPresent: Bool
var vExtenal = ExtenalClass()
var body: some View {
Button("Change-Me") {
vExtenal.ExtChangePrice()
// ContentView().fChangePrice()
}.padding()
Button("Return") { self.vPresent = false }
}
}
Im fairly new to Swift and I'm trying to produce a HStack (that will be used in a progress bar) of element and to be able to add elements with a button.
Im not sure if I should use a variable in the ForEach(1..<Variable) section or use another method. Here is the code I have so far but it did not work.
struct ContentView: View {
#State var fill : CGFloat = 0
#State var NumberOfCircles : Int = 0
var body: some View {
HStack(spacing:100) {
ForEach(0..<NumberOfCircles){ _ in
MyShape()
}
Button(action: {NumberOfCircles = 5}, label: {
Text("Button")
})
}
ForEach in SwiftUI needs a constant range to loop over. However,
as the error suggests, if you conform to Identifiable or use ForEach(_:id:content:) and provide an explicit id it is happy. So try this:
struct ContentView: View {
#State var fill: CGFloat = 0
#State var NumberOfCircles: Int = 0
var body: some View {
HStack(spacing: 20) {
ForEach(0..<NumberOfCircles, id: \.self){ _ in // <-- here
MyShape()
}
Button(action: {NumberOfCircles = 5}){
Text("Button")
}
}
}
}
I'm not sure what's your problem, but I tested this code and it works:
struct ContentView: View {
#State var numberOfCircles = 1
var body: some View {
VStack {
HStack {
ForEach(0..<numberOfCircles, id:\.self) { _ in
Circle()
.frame(width: 30, height: 30)
}
}
Button { numberOfCircles = 5 } label: {
Text("Add Circles")
}
}
}
}
Btw, naming convention for variables in Swift is camelCase. That means that declaring a variable you should name it numberOfCircles , not NumberOfCircles . The first uppercase letter is reserved for naming Classes, Structs and Protocols.
(new to kotlin) I'm making my own music app but i'm having an error that i don't understand :
in the bit of code where i try to access a random view (shuffle), i get the java.lang.IndexOutOfBoundsException: Index:96 Size:11.
96 in this example is the view in the listview that i'm trying to access.
It happens in this line : var iView = listViewMusic.get(idShuffle)
Same if i use listViewMusic[idShuffle]
EDIT : Turns out that 11 is only the 11 visible items on screen at any given moment, even if the list contains hundreds of items. When i use listViewMusic.smoothscrolltoposition(idShuffle) it works, but the size of 11 now relates to the 11 on screen after the scrolling
The function playNext() inside the activity, called when clicking on the shuffle button:
fun playNext(){
try {
// Find the order of the next song to play
var idShuffle = musicAdapter!!.getIdofItem(listMusicShuffled.get(musicNextToPlay).title)
// Find the right record in the list
//var iView = listViewMusic[idShuffle]
//toastIt(applicationContext, "adapter count ${listViewMusic.adapter.count}")
//toastIt(applicationContext, "listview count ${listViewMusic.count}")
var iView = listViewMusic.get(idShuffle) //throws the error
// Play it
playOrPause(iView)
// Prepare the next track
musicNextToPlay += 1
if (musicNextToPlay >= listMusicShuffled.size) {
musicNextToPlay = -1
}
} catch (e:Exception) {toastException(applicationContext, "playnext", e) }
}
The part of the function onCreate that fills in the listViewMusic:
// Retrieve the data
val mediaStoreUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val cursor = contentResolver.query(mediaStoreUri, null, "", null, null)
//Browse the data
listMusic = mutableListOf()
val listMusicJson = getMusicFromJson()
while (cursor!!.moveToNext()) {
val musicName = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME))
val musicArtist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST))
val musicUrl = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA))
val musicType:String =
if (musicArtist.contains("lmdmf", true)) { "LMDMF" }
else if (musicName.contains("slack", true)) { "SLACK" }
else { "MUSIC" }
listMusic.add(
MusicTrack(
musicName,
musicPlayCount,
musicUrl,
musicType
)
)
}
cursor.close()
musicAdapter = MusicAdapter(listMusic.sortedWith(compareBy<MusicTrack>{ it.title }).filter { it.type == "MUSIC"}.toMutableList())
listViewMusic.adapter = musicAdapter
I've been trying out Tornadofx. trying to create a custom title-bar, here's the code I'm currently trying
fun main(args: Array<String>) {
launch<MyApp>(args)
}
class MyApp : App(Title::class) {
override fun start(stage: Stage) {
stage.initStyle(StageStyle.UNDECORATED)
stage.minWidth = 600.0
stage.minHeight = 450.0
stage.isMaximized = false
super.start(stage)
}
}
class Title : View() {
private var xOffset = 0.0
private var yOffset = 0.0
private var screenBounds: Rectangle2D = Screen.getPrimary().visualBounds
private var originalBounds: Rectangle2D = Rectangle2D.EMPTY
init {
primaryStage.isMaximized = false
}
override val root = borderpane {
onMousePressed = EventHandler { ev ->
xOffset = primaryStage.x - ev.screenX
yOffset = primaryStage.y - ev.screenY
}
onMouseDragged = EventHandler { ev ->
primaryStage.x = xOffset + ev.screenX
primaryStage.y = yOffset + ev.screenY
}
center = label("Forms")
right = hbox {
button("Mi") {
action {
with(primaryStage) { isIconified = true }
}
}
button("Ma") {
action {
if (primaryStage.isMaximized) {
with(primaryStage) {
x = originalBounds.minX
y = originalBounds.minY
width = originalBounds.width
height = originalBounds.height
isMaximized = false
}
text = "Ma"
} else {
with(primaryStage) {
originalBounds = Rectangle2D(x, y, width, height)
x = screenBounds.minX
y = screenBounds.minY
width = screenBounds.width
height = screenBounds.height
isMaximized = true
}
text = "Re"
}
}
}
button("X") {
action {
app.stop()
println("exiting")
exitProcess(0)
}
}
}
}
}
the following work without problems
close
maximize, restore
restored window minimized, then open from taskbar
but when a maximized window is minimized to taskbar, then open from taskbar, it goes full screen(taskbar is hidden)
how do i fix this behavior, is there any part of my code that is wrong, needs change, or in need of any inclusions?
my configuration is Windows 10 64bit, Java 11.0.2, Kotlin 1.4.21, JavaFx 11.0.2, TornadoFx 1.7.20
I think this is a general problem in JavaFX (I mean not specific with TornadoFX).
The root cause for this is because of setting the maximized property of stage to true. Not sure what JavaFX internally does, but when you open the window from task bar and if the maximized value is true, then it renders in full screen mode.
You can fix this in two ways.
Approach #1:
When the window is opened from task bar, the iconfied property will turn off, set the stage dimensions again to screen bounds if maximized is true.
primaryStage.iconifiedProperty().addListener((obs,old,iconified)->{
if(!iconified && primaryStage.isMaximized()){
primaryStage.setWidth(screenBounds.getWidth());
primaryStage.setHeight(screenBounds.getHeight());
}
});
Approach #2:
Don't rely on the maximized property of the Stage. I believe you need that property to toggle the window dimensions. So instead maintain a instance variable to handle that.
boolean maximized = false;
ma.setOnAction(e -> {
if (maximized) {
// Set stage to original bounds
maximized = false;
ma.setText("Ma");
} else {
// Set stage to screen bounds
maximized = false;
ma.setText("Re");
}
});
A full working demo is below with both the approaches. You can decide which way to go based on your other requirments.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class UndecoratedWindowFullScreenDemo extends Application {
private double xOffset = 0.0;
private double yOffset = 0.0;
private Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds();
private Rectangle2D originalBounds = Rectangle2D.EMPTY;
private boolean maximized = false;
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
root.setStyle("-fx-background-color:pink;");
Scene scene = new Scene(root, 600, 450);
primaryStage.setScene(scene);
Label label = new Label("Forums");
Button mi = new Button("Mi");
Button ma = new Button("Ma");
Button x = new Button("X");
HBox pane = new HBox(mi, ma, x);
pane.setPadding(new Insets(3));
pane.setSpacing(5);
root.setCenter(label);
root.setRight(pane);
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setMinWidth(600);
primaryStage.setMinHeight(450);
primaryStage.setMaximized(false);
primaryStage.show();
root.setOnMousePressed(e -> {
xOffset = primaryStage.getX() - e.getScreenX();
yOffset = primaryStage.getY() - e.getScreenY();
});
root.setOnMouseDragged(e -> {
primaryStage.setX(xOffset + e.getScreenX());
primaryStage.setY(yOffset + e.getScreenY());
});
mi.setOnAction(e -> primaryStage.setIconified(true));
/* Use this approach if you want to go with the Stage maximized property */
// approach1(primaryStage, ma);
/* Use this approach if you want to avoid Stage maximized property and maintain a instance variable */
approach2(primaryStage, ma);
}
private void approach1(Stage primaryStage, Button ma) {
primaryStage.iconifiedProperty().addListener((obs, old, iconified) -> {
if (!iconified && primaryStage.isMaximized()) {
primaryStage.setWidth(screenBounds.getWidth());
primaryStage.setHeight(screenBounds.getHeight());
}
});
ma.setOnAction(e -> {
if (primaryStage.isMaximized()) {
primaryStage.setX(originalBounds.getMinX());
primaryStage.setY(originalBounds.getMinY());
primaryStage.setWidth(originalBounds.getWidth());
primaryStage.setHeight(originalBounds.getHeight());
primaryStage.setMaximized(false);
ma.setText("Ma");
} else {
originalBounds = new Rectangle2D(primaryStage.getX(), primaryStage.getY(), primaryStage.getWidth(), primaryStage.getHeight());
primaryStage.setX(screenBounds.getMinX());
primaryStage.setY(screenBounds.getMinY());
primaryStage.setWidth(screenBounds.getWidth());
primaryStage.setHeight(screenBounds.getHeight());
primaryStage.setMaximized(true);
ma.setText("Re");
}
});
}
private void approach2(Stage primaryStage, Button ma) {
ma.setOnAction(e -> {
if (maximized) {
primaryStage.setX(originalBounds.getMinX());
primaryStage.setY(originalBounds.getMinY());
primaryStage.setWidth(originalBounds.getWidth());
primaryStage.setHeight(originalBounds.getHeight());
maximized = false;
ma.setText("Ma");
} else {
originalBounds = new Rectangle2D(primaryStage.getX(), primaryStage.getY(), primaryStage.getWidth(), primaryStage.getHeight());
primaryStage.setX(screenBounds.getMinX());
primaryStage.setY(screenBounds.getMinY());
primaryStage.setWidth(screenBounds.getWidth());
primaryStage.setHeight(screenBounds.getHeight());
maximized = true;
ma.setText("Re");
}
});
}
}
There are two changes that were needed to solve the problem
The actual problem was that if isMaximized is set to true the app goes full screen when being open from task(minimized) even though isFullScreen property is separately available
Adding a maximized property listener so that we can invalidate if the isMaximized were to be ever modified by other means(like double clicking on title bar in Linux etc)
// CHANGE 1
stage.maximizedProperty().addListener { _, _, newValue ->
if (newValue) stage.isMaximized = false
}
by having a separate maximized instead of using isMaximized
// CHANGE 2
private var maximized: Boolean = false // <- here
if (maximized) { // <- here
// restore the window by setting bounds of original size
maximized = false // <- here
text = "Ma"
} else {
// maximize window by setting bounds from screen size
maximized = true // <- and here
text = "Re"
}
Bonus : use isFocusTraversable = false to make buttons that don't focus with keyboard traversal
Final solution
fun main(args: Array<String>) {
launch<MyApp>(args)
}
class MyApp : App(Window::class, Styles::class) {
override fun start(stage: Stage) {
stage.initStyle(StageStyle.UNDECORATED)
stage.minWidth = 600.0
stage.minHeight = 450.0
stage.width = 600.0
stage.height = 450.0
// CHANGE 1
stage.maximizedProperty().addListener { _, _, newValue ->
if (newValue) stage.isMaximized = false
}
stage.isMaximized = false
super.start(stage)
}
}
class Window : View() {
override val root = borderpane {
top = Title().root
}
}
class Title : View() {
// CHANGE 2
private var maximized: Boolean = false // <- here
private var xOffset = 0.0
private var yOffset = 0.0
private var screenBounds: Rectangle2D = Screen.getPrimary().visualBounds
private var originalBounds: Rectangle2D = Rectangle2D.EMPTY
init {
primaryStage.isMaximized = false
}
override val root = hbox {
hgrow = Priority.ALWAYS
onMousePressed = EventHandler { ev ->
xOffset = primaryStage.x - ev.screenX
yOffset = primaryStage.y - ev.screenY
}
onMouseDragged = EventHandler { ev ->
primaryStage.x = xOffset + ev.screenX
primaryStage.y = yOffset + ev.screenY
}
val l1 = hbox {
hgrow = Priority.ALWAYS
alignment = Pos.CENTER
label("Forms")
}
add(l1)
l1.requestFocus()
button("Mi") {
id = "min"
action {
with(primaryStage) { isIconified = true }
}
isFocusTraversable = false
}
button("Ma") {
id = "max"
action {
if (maximized) { // <- here
with(primaryStage) {
x = originalBounds.minX
y = originalBounds.minY
width = originalBounds.width
height = originalBounds.height
maximized = false // <- here
}
text = "Ma"
} else {
with(primaryStage) {
originalBounds = Rectangle2D(x, y, width, height)
x = screenBounds.minX
y = screenBounds.minY
width = screenBounds.width
height = screenBounds.height
maximized = true // <- and here
}
text = "Re"
}
l1.requestFocus()
}
isFocusTraversable = false
}
button("X") {
id = "close"
action {
app.stop()
println("exiting")
exitProcess(0)
}
isFocusTraversable = false
}
}
}
Has anyone created a custom View to use a picker? I would like to use many pickers without clogging my view definition.
Here is my easy to use custom view to use SwiftUI picker:
import SwiftUI
struct MyPicker : View {
#State var title:String = ""
#State var display:Bool = false
#State var chosenItem:Int = 0
var choices:[String] = []
var setPickerValue:(Int)->Void
var body: some View {
HStack {
Text("\(title)")
Button(action: {
self.display.toggle()
}) {
Text("\(choices[chosenItem])")
}
if display {
Picker(selection: $chosenItem, label:Text("")) {
ForEach(0 ..< choices.count) {
Text(self.choices[$0])
}
}
.onTapGesture{
self.display.toggle()
self.setPickerValue(self.chosenItem)
}
}
}
}
init(t:String, c:[String], initChoice: Int, funcSetValue:#escaping (Int)->Void) {
_title = State(initialValue: t)
choices = c
_chosenItem = State(initialValue: initChoice)
setPickerValue = funcSetValue
}
}
struct MyView : View {
#State var imageChoice:Int = 0
var imageList:[String] = ["imageOne", "imageTwo", "imageThree"]
var body: some View {
MyPicker(t:"Images", c:imageList, initChoice:0, funcSetValue:setImageChoice)
}
func setImageChoice(v:Int) -> Void {
imageChoice = v
}
}