SwiftUI - How to add a screen to a section of segmental control - swift

I have coded a currency converter page, however I want to add this converter to a section on a segmented control. How can I add this page to one section in a segmented control so one section of the control is currency conversion and then when I click on the other section it will take me to another page?
import SwiftUI
struct ContentView: View {
#State var input = "100"
#State var base = "USD"
#State var currencyList = [String]()
#FocusState private var inputIsFocused: Bool
func makeRequest (showAll: Bool, currencies: [String] = ["USD", "GBP", "EUR"]) {
apiRequest(url:
"https:api.exchangerate.host/latest?base=\(base)&amount=\(input)") { currency in
var tempList = [String]()
for currency in currency.rates {
if showAll {
tempList.append("\(currency.key) \(String(format: "%.2f", currency.value))")
} else if currencies.contains(currency.key) {
tempList.append("\(currency.key) \(String(format: "%.2f", currency.value))")
}
tempList.sort()
}
currencyList.self = tempList
print(tempList)
}
}
var body: some View {
VStack {
HStack {
Text("Currencies")
.font(.system(size: 30))
.bold()
Image(systemName: "yensign.square")
.font(.system(size: 30))
.foregroundColor(.black)
}
List {
ForEach(currencyList, id: \.self) { currency in
Text(currency)
}
}
VStack {
Rectangle()
.frame(height: 8.0)
.foregroundColor(.black)
.opacity(0.90)
TextField("Enter an Amount", text: $input)
.padding()
.background(Color.gray.opacity(0.10))
.cornerRadius(20.0)
.padding()
.keyboardType(.decimalPad)
.focused($inputIsFocused)
TextField("Enter a Currency", text: $base)
.padding()
.background(Color.gray.opacity(0.10))
.cornerRadius(20.0)
.padding()
.focused($inputIsFocused)
Button("Convert") {
makeRequest(showAll: true, currencies: ["DKK", "SEK" ,"NOK"])
inputIsFocused = false
}.padding()
}
}.onAppear {
makeRequest(showAll: true)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View{
ContentView()
}
}
`
I built the segmented control separate to the converter, however one was built with SwiftUi and the other was built using storyboard, so How can I combine the two?

Related

Trying to create a grid of numbers that must be clicked in order and when clicked the background changes color SWIFTUI

As the title says I am trying to create a grid of random numbers that must be clicked in ascending order and when pressed the background changes color so they're no longer visible.
I figured out how to create the grid from 0 to what ever size grid I want (5x5, 10x10, etc...)
I want the background to be white to start and then when the button is pressed it changes to black
The two biggest issues I'm having are all of the buttons turning black after I press on one button and the numbers randomize every time I press a button.
Any one have any ideas?
import SwiftUI
struct ContentView: View {
#State var buttonbackg = Color.white
#State var gridsize = 100
var body: some View {
//make grid
let cellCount = (gridsize/10)
let r = numbrand(min: 00, max: ((gridsize/10) * (gridsize/10) - 1))
ZStack{
Rectangle()
.ignoresSafeArea()
.background(Color.black)
VStack{
Text("Concentration Grid")
.font(.title)
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.padding(.bottom)
Spacer()
}
VStack(spacing:3) {
Spacer()
ForEach(1...cellCount, id:\.self) { i in
HStack(spacing:3) {
Spacer()
ForEach(1...cellCount, id:\.self) { c in
let a = r()
ZStack {
Button(action:{
if (self.buttonbackg == .white) {
self.buttonbackg = .black
}
}){
Text("\(a)")
.foregroundColor(Color.black)
.frame(width: 28, height: 28)
.background(buttonbackg)
.cornerRadius(5)
.multilineTextAlignment(.center)
.scaledToFit()
.padding(.all, -2)
Spacer()
}
Spacer()
}
}
}
Spacer()
}
Spacer()
}
}
}
//^grid
func numbrand(min: Int, max:Int) -> () -> Int{
//let array: [Int] = Array(min...max)
var numbers: [Int] = []
return {
if numbers.isEmpty {
numbers = Array(min...max)
}
let index = Int(arc4random_uniform(UInt32(numbers.count)))
return numbers.remove(at: index)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Every-time you click a button, view redraws itself and thus your randomArray is created again
You need to create a Struct here which holds the property whether the
button is selected or not.
Give it a try this way:
struct concentrationGame: Hashable {
var id = UUID()
var number: Int = 0
var isSelected: Bool = false
}
class RandomNumbers: ObservableObject {
#Published var randomNumbers : [concentrationGame]
init() {
self.randomNumbers = (1...100).map{_ in arc4random_uniform(100)}.map({ concentrationGame(number: Int($0)) })
}
}
struct ContentView: View {
#ObservedObject var randomNumbers = RandomNumbers()
private var gridItemLayout = [GridItem(.adaptive(minimum: 50))]
var body: some View {
NavigationView {
ScrollView {
LazyVGrid(columns: gridItemLayout, spacing: 20) {
ForEach($randomNumbers.randomNumbers, id: \.self) { $randomNumbers in
Button(String(randomNumbers.number)) { randomNumbers.isSelected.toggle() }
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 50)
.foregroundColor(.white)
.background( randomNumbers.isSelected ? .white : .black)
.cornerRadius(10)
}
}
}
.padding()
.ignoresSafeArea(edges: .bottom)
.navigationTitle("Concentration Grid")
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

navigationLink to tag in another view

Here's an edited version of the question. I'm working in Swiftui and I have two views. The first has two NavigationLinks. The second link is where I get stuck. I would like that link to go to 'tabTwo' on PageTwo. Getting to PageTwo isn't an issue... it's getting to tabTwo as a view on that page.
Here's an edited down contentView file:
import SwiftUI
struct ContentView: View {
#State var isNavigationBarHidden: Bool = true
#State private var selectedTab = ""
var body: some View {
NavigationView {
ZStack {
VStack(spacing: 0) {
VStack(spacing: 0) {
NavigationLink("Link One", destination: PageTwo()).padding(10)
.foregroundColor(Color.gray)
.background(Color.white)
Divider().frame(width: 300,height: 1).background(Color.black)
}
VStack(spacing: 0) {
NavigationLink("Link Two", destination: PageTwo())
.padding(10)
.foregroundColor(Color.gray)
.background(Color.white)
}
// If you remove the below VStack everything works.
VStack(spacing: 0) {
NavigationLink("Page Two tabTwo", destination: PageTwo(), tag: "tabTwo", selection: $selectedTab?)
.padding(10)
.foregroundColor(Color.gray)
.background(Color.white)
}
}
}
}
.navigationBarTitle("Hidden Title")
.navigationBarHidden(self.isNavigationBarHidden)
.onAppear {self.isNavigationBarHidden = true}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and here's an edited down PageTwo:
import SwiftUI
struct PageTwo: View {
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
#State var isNavigationBarHidden: Bool = true
#State private var selectedTab = ""
var body: some View {
ZStack() {
TabView(selection: $selectedTab) {
//---> Tab One
VStack(spacing: 0) {
VStack{
Text("PAGE ONE")
.padding(.bottom, 20)
}
Button(action: {
self.mode.wrappedValue.dismiss()})
{ Text("BACK") }
.font(Font.system(size:14, weight: .bold, design: .default))
}
.navigationBarTitle("Hidden Title")
.navigationBarHidden(self.isNavigationBarHidden)
.onAppear {self.isNavigationBarHidden = true}
.tabItem {Label("ONE", systemImage: "circle.fill")}
.tag("tabOne")
//---> Tab Two
VStack(spacing: 0) {
VStack{
Text("PAGE TWO")
.padding(.bottom, 20)
}
Button(action: {
self.mode.wrappedValue.dismiss()})
{ Text("BACK") }
.font(Font.system(size:14, weight: .bold, design: .default))
}
.navigationBarTitle("Hidden Title")
.navigationBarHidden(self.isNavigationBarHidden)
.onAppear {self.isNavigationBarHidden = true}
.tabItem {Label("TWO", systemImage: "circle.fill")}
.tag("tabTwo")
}
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(.page(backgroundDisplayMode: .never))
.onAppear {self.isNavigationBarHidden = true}
}
.navigationBarTitle("Hidden Title")
.navigationBarHidden(self.isNavigationBarHidden)
.onAppear {self.isNavigationBarHidden = true}
.background(Color.gray)
.opacity(0.75)
.indexViewStyle(.page(backgroundDisplayMode: .never))
}
}
struct PageTwo_Previews: PreviewProvider {
static var previews: some View {
PageTwo()
}
}
This throws an error at the 'some view' level of:
Failed to produce diagnostic for expression; please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the project
I've submitted the report, but can't figure out how to do what I'd like. Any help is much appreciated.

Getting Values from own PickerView

I'm new to Swift and I'm currently developing my own Timer Application for practice purposes. (I do it without storyboard)
Now I have the Problem that i made a View called "TimePickerView" (Code below), where I created my own Picker. Then I use that TimePickerView in another part of my Application with other Views (in a View). In that View I want to pick my time but I don't know how i can get the Values of the Picker (The Picker works by the way)
This is my TimePickerView
import SwiftUI
struct TimePickerView: View {
#State private var selectedTimeIndexSecond = 0
#State private var selectedTimeIndexMinutes = 0
#State private var seconds : [Int] = Array(0...59)
#State private var minutes : [Int] = Array(0...59)
var body: some View {
VStack{
Text("Select Your Time")
HStack{
//minutes-Picker
Picker("select time", selection: $selectedTimeIndexMinutes, content: {
ForEach(0..<minutes.count, content: {
index in
Text("\(minutes[index]) min").tag(index)
})
})
.padding()
.frame(width: 120)
.clipped()
//seconds-Picker
Picker("select time", selection: $selectedTimeIndexSecond, content: {
ForEach(0..<seconds.count, content: {
index in
Text("\(seconds[index]) sec").tag(index)
})
})
.padding()
.frame(width: 120)
.clipped()
Spacer()
}
Text("You picked the time")
.multilineTextAlignment(.center)
.font(.title2)
.padding()
Text("\(minutes[selectedTimeIndexMinutes]) min : \(seconds[selectedTimeIndexSecond]) sec")
.font(.title)
.bold()
.padding(.top, -14.0)
}
}
func getValues() -> (Int, Int) {
return (self.minutes[selectedTimeIndexMinutes] ,self.seconds[selectedTimeIndexSecond])
}
}
and that is the View I want to use my Picker, but I don't know how I get those values from the Picker:
struct SetTimerView: View {
#State var timepicker = TimePickerView()
var body: some View {
NavigationView{
VStack{
//Select the time
timepicker
//Timer variables (This doesn't work)
var timerTime = timepicker.getValues()
var minutes = timerTime.0
var seconds = timerTime.1
Spacer()
let valid : Bool = isValid(timerTime: minutes+seconds)
//Confirm the time
NavigationLink(
destination:
getRightView(
validBool: valid,
timerTime: minutes*60 + seconds),
label: {
ConfirmButtonView(buttonText: "Confirm")
});
Spacer()
}
}
}
func isValid(timerTime : Int) -> Bool {
if (timerTime == 0) {
return false
} else {
return true
}
}
#ViewBuilder func getRightView(validBool : Bool, timerTime : Int) -> some View{
if (validBool == true) {
TimerView(userTime: CGFloat(timerTime), name: "David", isActive: true)
} else {
UnvalidTimeView()
}
}
}
I think main problem is misunderstanding conceptions between data and views.
At first you need a model witch will override your data (create it in separate swift file):
import Foundation
class Time: ObservableObject {
#Published var selectedTimeIndexMinutes: Int = 0
#Published var selectedTimeIndexSecond: Int = 0
}
Pay attention on ObservableObject so that swiftUI can easily detect changes to it that trigger any active views to redraw.
Next I try to change the value of the model in the view
import SwiftUI
struct TimePickerView: View {
#EnvironmentObject var timeData: Time
#State private var seconds : [Int] = Array(0...59)
#State private var minutes : [Int] = Array(0...59)
var body: some View {
VStack{
Text("Select Your Time")
HStack{
//minutes-Picker
Picker("select time", selection: $timeData.selectedTimeIndexMinutes, content: {
ForEach(0..<minutes.count, content: {
index in
Text("\(minutes[index]) min").tag(index)
})
})
.padding()
.frame(width: 120)
.clipped()
//seconds-Picker
Picker("select time", selection: $timeData.selectedTimeIndexSecond, content: {
ForEach(0..<seconds.count, content: {
index in
Text("\(seconds[index]) sec").tag(index)
})
})
.padding()
.frame(width: 120)
.clipped()
Spacer()
}
Text("You picked the time")
.multilineTextAlignment(.center)
.font(.title2)
.padding()
Text("\(timeData.selectedTimeIndexMinutes) min : \(timeData.selectedTimeIndexSecond) sec")
.font(.title)
.bold()
.padding(.top, -14.0)
}
}
}
struct TimePickerView_Previews: PreviewProvider {
static var previews: some View {
TimePickerView()
.environmentObject(Time())
}
}
Like you can see I don't using #Blinding, instead of it I connecting our Model with a View
On the next view I can see changes, I created a new one because your example have view that don't indicated here...
import SwiftUI
struct ReuseDataFromPicker: View {
#EnvironmentObject var timeData: Time
var body: some View {
VStack{
Text("You selected")
Text("\(timeData.selectedTimeIndexMinutes) min and \(timeData.selectedTimeIndexSecond) sec")
}
}
}
struct ReuseDataFromPicker_Previews: PreviewProvider {
static var previews: some View {
ReuseDataFromPicker()
.environmentObject(Time())
}
}
And collect all in a Content View
struct ContentView: View {
var body: some View {
TabView {
TimePickerView()
.tabItem {Label("Set Timer", systemImage: "clock.arrow.2.circlepath")}
ReuseDataFromPicker()
.tabItem {Label("Show Timer", systemImage: "hourglass")}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(Time())
}
}
Like that you can easily change or reuse your data on any other views

SwiftUI: The View doesn't refresh the Body when segmented Picker has a new Position

My App calculate for divers some important Values like max. depth etc in meter or in feet. The calculation is written in a separate File. I have a Problem with the segmented Picker toggle. If I launch the App the segmented Picker (Metric <-> Imperial) is on Metric. When I change the segmented Picker on Imperial nothing happened. But when I click on the Button and change the Value (32%, 33%) and push done the results stands in ft. . When I change the segmented Picker Back to Metric nothing happened again. The View is only updated when I change the Value (32%, 33%, …), but I want that the Body refresh when the segmented Picker (Metric <-> Imperial) has a new "position".
The App UI
import SwiftUI
import Combine
struct ContentView: View {
#State var unitSelection = UserDefaults.standard.integer(forKey: "Picker")
#State var O2_2 = 32
#State var PO2_2 = 1.2
var body: some View {
ZStack {
VStack {
Picker("", selection: $unitSelection) {
Text("Metric").tag(0)
Text("Imperial").tag(1)
}
.pickerStyle(SegmentedPickerStyle()).padding(.horizontal, 89)
.onReceive(Just(unitSelection)) {
UserDefaults.standard.set($0, forKey: "Picker")
switchMeasurement(measurement: $0)
}
Spacer()
}
BasicStructure(valueIndexO2Calculate_3: $O2_2, PO2_1: $PO2_2, unitSelection_1: unitSelection)
}
}
}
struct BasicStructure: View {
//Variable from Picker: Calculate -> O2
#Binding var valueIndexO2Calculate_3: Int
#Binding var PO2_1: Double
var unitSelection_1: Int
var body: some View {
VStack {
HStack {
Spacer()
}
Calculate(O2: $valueIndexO2Calculate_3, PO2: $PO2_1, unitSelection_2: unitSelection_1)
ButtonMOD(valueIndexO2Calculate_2: self.$valueIndexO2Calculate_3, unitSelection_0: unitSelection_1)
Spacer()
}
}
}
struct Calculate: View {
#Binding var O2: Int
#Binding var PO2: Double
var unitSelection_2: Int
var body: some View {
VStack {
//O2 max. depth
HStack (alignment: .center){
VStack(alignment: .leading) {
Text("O2 max. depth")
Text("MOD O2")
}
Spacer()
Text(calculateMod02(o2: self.O2, po2: Float(self.PO2)))
.padding(.trailing)
}
.padding(.top).padding(.horizontal)
Divider()
//eq. air depth
HStack(alignment: .center) {
VStack(alignment: .leading) {
Text("eq. air depth")
Text("EAD")
}
Spacer()
Text(calculateEAD(o2: self.O2, po2: Float(self.PO2)))
.padding(.trailing)
}
.padding(.horizontal)
Divider()
//no deco time
HStack(alignment: .center) {
VStack(alignment: .leading) {
Text("no deco time")
Text("DECO 2000")
}
Spacer()
Text("\(calculateDeco2000(o2: self.O2, po2: Float(self.PO2)), specifier: "%.0f")'")
.padding(.trailing)
}
.padding(.horizontal)
Divider()
//max. dive time
HStack(alignment: .center) {
VStack(alignment: .leading) {
Text("max. dive time")
Text("CNS")
}
Spacer()
Text("\(calculateCNS(o2: self.O2, po2: Float(self.PO2)), specifier: "%.0f")'")
.padding(.trailing)
}
.padding(.horizontal).padding(.bottom)
}
.padding()
}
}
struct ButtonMOD: View {
#State var showingDetail_O2 = false
#State var showingDetail_PO2 = false
//Variable from Picker: Calculate -> O2
#Binding var valueIndexO2Calculate_2: Int
var unitSelection_0: Int
var body: some View {
VStack {
HStack(alignment: .center) {
VStack(alignment: .leading) {
Button(action: {
self.showingDetail_O2.toggle()
}) {
Text("\(self.valueIndexO2Calculate_2)%")
Text("O2")
}.sheet(isPresented: $showingDetail_O2) {
SheetViewO2(showSheetView: self.$showingDetail_O2, valueIndexO2Calculate_1: self.$valueIndexO2Calculate_2, unitSelection_3: self.unitSelection_0)
}
}
}
}.padding()
.padding()
}
}
struct SheetViewO2: View {
#Binding var showSheetView: Bool
//Variable from Picker: Calculate -> O2
#Binding var valueIndexO2Calculate_1: Int
var unitSelection_3: Int
var body: some View {
NavigationView {
ValueO2(valueIndexO2Calculate_0: $valueIndexO2Calculate_1, unitSelection_4: unitSelection_3)
.navigationBarTitle(Text("Select value"), displayMode: .inline)
.navigationBarItems(trailing: Button(action: {
self.showSheetView = false
}) {
Text("Done")
.bold()
})
}.navigationViewStyle(StackNavigationViewStyle())
}
}
//Show "Picker O2"
struct ValueO2: View {
//Variable from Picker: Calculate -> O2
#Binding var valueIndexO2Calculate_0: Int
var unitSelection_4: Int
#State var valueArray : [Int] = []
var body: some View {
VStack{
Section {
Text("O2")
Picker("",selection: $valueIndexO2Calculate_0) {
ForEach(valueArray, id: \.self){ value in
Text("\(value) %")
}
}
}
.labelsHidden()
}.onAppear{
self.initPickerIndex()
}
}
func initPickerIndex(){
valueArray = []
for index1 in 21..<50 {
valueArray.append(index1)
}
for index2 in 1...5{
valueArray.append(40 + index2 * 10)
}
}
}
You don't need binding in this scenario, use direct value for BasicStructure and below (ie. similarly in subviews), like
BasicStructure(valueIndexO2Calculate_3: $O2_2, PO2_1: $PO2_2,
unitSelection_1: unitSelection) // << here !!
and
struct BasicStructure: View {
//Variable from Picker: Calculate -> O2
#Binding var valueIndexO2Calculate_3: Int
#Binding var PO2_1: Double
var unitSelection_1: Int // << here !!
thus changing value in picker makes body refresh and dependent subviews rebuild.

SwiftUI passing a saved value to parent view

I am fairly new to SwiftUI I am trying to figure out the best way to pass data from a child view to parent?
Thanks for the help I come from a Javascript (React) background so this is a little different for me
The way my child view works is the user clicks on an image to select that image.
I have #State binding that saves the imgUrl which is a String referring to name in Assets.
I am just not sure about the best way to pass that value to the parent component.
Here is the child view (imageSelector)
struct ImageSelector: View {
#State private var windowImgs = ["1", "2", "3","4","5","6","7","8","9","10","11","12","13", "14","15","16","17","18"]
#State private var imgPicked = ""
var body: some View{
ScrollView(Axis.Set.horizontal, showsIndicators: true){
HStack{
ForEach(0..<18){num in
Button(action:{
self.imgPicked = self.windowImgs[num]
print(self.imgPicked)
}){
Image("\(self.windowImgs[num])")
.renderingMode(.original)
.resizable()
.cornerRadius(4)
.frame(width: 100, height: 100)
}
}
}
}
}
}
Here is the parent view (AddCounterForm)
struct AddCounterForm: View {
#Environment(\.presentationMode) var presentationMode
#State private var pickedImg: String = "defaultImg"
#State private var price: String = "0.0"
#State private var qty: String = "0"
var body: some View {
VStack (spacing: 40){
HStack {
Button("Cancel"){
self.presentationMode.wrappedValue.dismiss()
}
.foregroundColor(.red)
Spacer()
Button("Save"){
}
}
HStack {
VStack (spacing: 20){
TextField("Window type", text: /*#START_MENU_TOKEN#*//*#PLACEHOLDER=Value#*/.constant("")/*#END_MENU_TOKEN#*/)
TextField("Window location", text: /*#START_MENU_TOKEN#*//*#PLACEHOLDER=Value#*/.constant("")/*#END_MENU_TOKEN#*/)
}
.textFieldStyle(RoundedBorderTextFieldStyle())
Image(pickedImg)
.resizable()
.cornerRadius(4)
.frame(width: 90, height: 90)
.padding(.leading)
}
HStack {
Text("Price")
TextField("", text:$price)
.frame(width: 70)
.textFieldStyle(RoundedBorderTextFieldStyle())
.keyboardType(.numberPad)
Spacer()
Text("Qty")
TextField("", text:$qty)
.frame(width: 70)
.textFieldStyle(RoundedBorderTextFieldStyle())
.keyboardType(.numberPad)
}
VStack {
Text("Select an image")
.foregroundColor(.blue)
ImageSelector()
.padding(.bottom)
Button("Use your own image"){
//method
}
.frame(width: 180, height: 40)
.background(Color.blue)
.clipShape(Capsule())
.foregroundColor(.white)
.padding(.top)
}
}
.padding()
}
}
Solution for preview thanks for the help from #Asperi & #neverwinterMoon
struct ImageSelector_Previews: PreviewProvider {
static var previews: some View {
PreviewWrapper()
}
}
struct PreviewWrapper: View {
#State(initialValue: "") var imgPicked: String
var body: some View {
ImageSelector(imgPicked: $imgPicked)
}
}
In this case Binding is most appropriate
struct ImageSelector: View {
#Binding var imgPicked: String
and use
ImageSelector(imgPicked: $pickedImg)
.padding(.bottom)