How to link two min max sliders with swiftui iOS15 - slider

Image: Sliders
I have two sliders to filter the results. One of the sliders sets minimum value another sets maximum value. Filters work properly, except min value can be increased to a value more than max and max can be lowered below min.
How can I link these sliders so that min does not exceed max and max does not go below min?
Here is the code:
#State private var minimum: Double = 98.0
#State private var maximum: Double = 1000000.0
var filteredProducts: [Product]
var body: some View {
List {
Section(header: Text("Filters")){
DisclosureGroup("Price range") {
Slider(value: $minimum, in: 98...1000000) {
Text("\(minimum)")
} minimumValueLabel: {
Text("min")
} maximumValueLabel: {
Text("\(minimum, specifier: "%.0f")")
}
Slider(value: $maximum, in: 98...1000000) {
Text("\(maximum)")
} minimumValueLabel: {
Text("max")
} maximumValueLabel: {
Text("\(maximum, specifier: "%.0f")")
}
}
}
}
}
var slideresults: [Product] {
if minimum == 0 && maximum == 1000000000.0 {
return filteredProducts
} else {
return filteredProducts.filter {
$0.price > minimum &&
$0.price < maximum
}
}
}

I see you asked it a long time ago, hope still this can help.
I had the same problem and this is how I solved it. (showing on your code)
#State private var minimum: Double = 98.0
#State private var maximum: Double = 1000000.0
var filteredProducts: [Product]
var body: some View {
List {
Section(header: Text("Filters")){
DisclosureGroup("Price range") {
Slider(value: $minimum, in: 98...maximum) {
Text("\(minimum)")
} minimumValueLabel: {
Text("min")
} maximumValueLabel: {
Text("\(maximum, specifier: "%.0f")")
}
Slider(value: $maximum, in: minimum...1000000) {
Text("\(maximum)")
} minimumValueLabel: {
Text("\(minimum, specifier: "%.0f")
} maximumValueLabel: {
Text("\(maximum, specifier: "%.0f")")
}
}
}
}
}
Basically, give your Min value Slider your #state max value and vice versa for the Max value Slider. And put corresponding labels: max value label to min value slider and vice versa.

Related

SwiftUI VIEW change variable from external class

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 }
}
}

How to get object of Maximum value from LiveData?

I have liveData of market data. I want one market data object which have highest 'volume'. Here, volume is string value("277927.5793846733451135"), it could be null also.
I am using below code to achieve this. but, its not working.
viewModel.marketlist.observe(this as LifecycleOwner, { marketdata ->
val marketData = marketdata.getOrNull()
if(marketData !=null) {
val mData: MarketData? = marketData.marketData?.maxByOrNull { checkNotNull(it.volume) }
if (mData != null) {
binding.textViewPrice.text = mData.price
}
}
else {
//TODO
}
})
Any help would be appreciated!
You should be able to do something like this:
viewModel.marketList.observe(viewLifecycleOwner) { marketData ->
val maxData = marketData.getOrNull()?.marketData?.let { dataValues ->
dataValues.maxByOrNull { it.volume?.toDoubleOrNull() ?: -1.0 }
}
if (maxData != null) {
binding.textViewPrice.text = maxData.price
}
}
I cleaned up the observe call a bit, then I'm checking if marketData.getOrNull().marketData is null right away with my let { ... } block.
If you do have marketData (the inner one), it'll then safely call maxByOrNull { it.volume }.

max string length in compose before clipping

I want to create a string with the max number of characters allowable in the box.
setContent {
ViewincomposetestTheme {
var size by remember { mutableStateOf(IntSize.Zero) }
var widthdp by remember { mutableStateOf(0.dp) }
BoxWithConstraints(Modifier.fillMaxSize().background(Color.Yellow)) {
val widthOfChar = 13 // how do I find this
var string by remember {
mutableStateOf(
StringBuffer()
.apply {
repeat(maxWidth.value.toInt() / widthOfChar) { append("H") }
}
.toString()
)
}
Text(string)
}
}
}
You can create a separate Text to calculate the size of a character, which will report you its size with onTextLayout. Using drawWithContent you can prevent it from being drawn.
Also in your example you get the width using maxWidth.value.toInt(): here you get the value of dp, not pixels. You could convert it using LocalDensity, but you also get the pixel value directly from BoxWithConstraints using constraints.maxWidth.
BoxWithConstraints(
Modifier
.fillMaxSize()
.background(Color.Yellow)
) {
var charWidth by remember { mutableStateOf<Int?>(null) }
val string = remember(maxWidth, charWidth) {
charWidth?.let { charWidth ->
StringBuffer()
.apply {
repeat(constraints.maxWidth / charWidth) { append("H") }
}
.toString()
}
}
Text(
"H",
onTextLayout = {
charWidth = it.size.width
},
modifier = Modifier.drawWithContent { }
)
string?.let {
Text(string)
}
}
You can just use the maxWidth parameter of the BoxWithConstraints, then convert the obtained dp value toPx(). Decide a textsize for the TextField in sp and then do whatever calculations you want to do after converting that to Px as well. Max characters will be maxWidth / textSize, roughly.

How to add elements to a HStack Using ForEach SwiftUi

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.

SwiftUI: Is it possible to automatically move to the next textfield after 1 character is entered?

I trying to make a SwiftUI app where after entering one letter in a TextField the cursor automatically moves to the next TextField. The UI is pretty much like this.
In Swift/IB, it looks like this was done with delegates and adding a target like in this post:
How to move to the next UITextField automatically in Swift
But can't find any documentation for using delegates/targets in SwiftUI.
I tried following this post:
SwiftUI TextField max length
But this has not worked for me. Setting the .prefix(1) does not seem to make a difference. The TextField still accepts any amount of characters and when moved to the next TextField does not reduce the characters entered to only the first character.
In SwiftUI's current state, is it possible to automatically move to the next TextField after 1 character is entered?
Thanks for any help!
It can be done in iOS 15 with FocusState
import SwiftUI
///Sample usage
#available(iOS 15.0, *)
struct PinParentView: View {
#State var pin: Int = 12356
var body: some View {
VStack{
Text(pin.description)
PinView(pin: $pin)
}
}
}
#available(iOS 15.0, *)
struct PinView: View {
#Binding var pin: Int
#State var pinDict: [UniqueCharacter] = []
#FocusState private var focusedField: UniqueCharacter?
var body: some View{
HStack{
ForEach($pinDict, id: \.id, content: { $char in
TextField("pin digit", text:
Binding(get: {
char.char.description
}, set: { newValue in
let newest: Character = newValue.last ?? "0"
//This check is only needed if you only want numbers
if Int(newest.description) != nil{
char.char = newest
}
//Set the new focus
DispatchQueue.main.async {
setFocus()
}
})
).textFieldStyle(.roundedBorder)
.focused($focusedField, equals: char)
})
}.onAppear(perform: {
//Set the initial value of the text fields
//By using unique characters you can keep the order
pinDict = pin.description.uniqueCharacters()
})
}
func setFocus(){
//Default to the first box when focus is not set or the user reaches the last box
if focusedField == nil || focusedField == pinDict.last{
focusedField = pinDict.first
}else{
//find the index of the current character
let idx = pinDict.firstIndex(of: focusedField!)
//Another safety check for the index
if idx == nil || pinDict.last == pinDict[idx!]{
focusedField = pinDict.first
}else{
focusedField = pinDict[idx! + 1]
}
}
//Update the Binding that came from the parent
setPinBinding()
}
///Updates the binding from the parent
func setPinBinding(){
var newPinInt = 0
for n in pinDict{
if n == pinDict.first{
newPinInt = Int(n.char.description) ?? 0
}else{
newPinInt = Int(String(newPinInt) + n.char.description) ?? 0
}
}
pin = newPinInt
}
}
//Convert String to Unique characers
extension String{
func uniqueCharacters() -> [UniqueCharacter]{
let array: [Character] = Array(self)
return array.uniqueCharacters()
}
func numberOnly() -> String {
self.trimmingCharacters(in: CharacterSet(charactersIn: "-0123456789.").inverted)
}
}
extension Array where Element == Character {
func uniqueCharacters() -> [UniqueCharacter]{
var array: [UniqueCharacter] = []
for char in self{
array.append(UniqueCharacter(char: char))
}
return array
}
}
//String/Characters can be repeating so yu have to make them a unique value
struct UniqueCharacter: Identifiable, Equatable, Hashable{
var char: Character
var id: UUID = UUID()
}
#available(iOS 15.0, *)
struct PinView_Previews: PreviewProvider {
static var previews: some View {
PinParentView()
}
}