SwiftUI: onChange with #Binding from outside view - swift

Updated to provide full reproducible example.
I have a view MainView with two sub-views, MainTextView and TableOfContentsView. I want to select an item in TableOfContentsView which triggers a scroll position change in MainTextView.
I have an #State var scrollPosition: Int in MainView which is passed to an #Binding var scrollPosition: Int in MainTextView. Here is the code for my 3 views.
struct MainView: View {
#State private var showingToc = false
#State private var scrollPosition = 0
var body: some View {
VStack(alignment: .leading) {
Text("Table of Contents (chapter \(scrollPosition + 1))")
.onTapGesture {
showingToc = !showingToc
}
Divider()
if showingToc {
TableOfContentsView(
onChapterSelected: { chapter in
scrollPosition = chapter
showingToc = false
}
)
} else {
MainTextView(scrollPosition: $scrollPosition)
}
}
}
}
struct TableOfContentsView: View {
var onChapterSelected: (Int) -> Void
var body: some View {
ScrollView {
VStack {
ForEach(0 ..< 20) { i in
Text("Chapter \(i + 1)")
.onTapGesture {
onChapterSelected(i)
}
}
}
}
}
}
struct MainTextView: View {
let lorumIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
#Binding var scrollPosition: Int
#State private var scrollProxy: ScrollViewProxy? = nil
var body: some View {
ScrollView {
ScrollViewReader { proxy in
VStack(alignment: .leading) {
ForEach(0 ..< 20) { i in
VStack(alignment: .leading) {
Text("Chapter \(i + 1)")
.frame(maxWidth: .infinity, alignment: .center)
.padding(.top)
Text(lorumIpsum)
.font(.system(size: 18, design: .serif))
.padding(.top)
}
.padding(.bottom, 20)
.id(i)
}
}
.padding(.leading)
.padding(.trailing)
.onAppear {
scrollProxy = proxy
}
}
}
.onChange(of: scrollPosition) { target in
scrollProxy?.scrollTo(target, anchor: .top)
}
}
}
My .onChange function is never called - if I place a breakpoint inside, it never hits the breakpoint. What am I missing here? How do I observe an #Binding that is changed from outside of the view?

.onChange is not going to get called cause MainTextView doesn't exist when showingToc = true because of the conditional block:
if showingToc {
TableOfContentsView(onChapterSelected: { chapter in
scrollPosition = chapter
showingToc = false
}
)
} else {
MainTextView(scrollPosition: $scrollPosition)
}
You might want to consider showing TOC as an overlay. But to get your current code working, you need to call proxy.scrollTo in your .onAppear of MainTextView:
.onAppear {
scrollProxy = proxy
scrollProxy?.scrollTo(scrollPosition, anchor: .top)
}

Related

SwiftUI Popup window

I created my first simple card game.
https://codewithchris.com/first-swiftui-app-tutorial/
Now I want to add a pop up window which will pop up with the message "You win" if the player gets 15 points and "You lose" if the CPU gets 15 points first.
Can someone please help me how to do it?
I would be glad if there is some tutorial so I can do it myself, not just copy and paste it.
import SwiftUI
struct ContentView: View {
#State private var playCard = "card5"
#State private var cpuCard = "card9"
#State private var playerScore = 0
#State private var cpuScore = 0
var body: some View {
ZStack {
Image("background-plain")
.resizable()
.ignoresSafeArea()
VStack{
Spacer()
Image("logo")
HStack{
Spacer()
Image(playCard)
Spacer()
Image(cpuCard)
Spacer()
}
Button(action: {
//reset
playerScore = 0
cpuScore = 0
}, label: {
Image(systemName: "clock.arrow.circlepath")
.font(.system(size: 60))
.foregroundColor(Color(.systemRed)) })
Button(action: {
//gen. random betw. 2 and 14
let playerRand = Int.random(in: 2...14)
let cpuRand = Int.random(in: 2...14)
//Update the cards
playCard = "card" + String(playerRand)
cpuCard = "card" + String(cpuRand)
//Update the score
if playerRand > cpuRand {
playerScore += 1
}
else if cpuRand > playerRand {
cpuScore += 1
}
}, label: {
Image("button")
})
HStack{
Spacer()
VStack{
Text("Player")
.font(.headline)
.padding(.bottom, 10.0)
Text(String(playerScore))
.font(.largeTitle)
}
Spacer()
VStack{
Text("CPU")
.font(.headline)
.padding(.bottom, 10.0)
Text(String(cpuScore))
.font(.largeTitle)
}
Spacer()
}
.foregroundColor(.white)
Spacer()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Make your popup like a view. And after that. Call it in method present a view.
struct ContentView : View {
#State var showingPopup = false // 1
var body: some View {
ZStack {
Color.red.opacity(0.2)
Button("Push me") {
showingPopup = true // 2
}
}
.popup(isPresented: $showingPopup) { // 3
ZStack { // 4
Color.blue.frame(width: 200, height: 100)
Text("Popup!")
}
}
}
}
extension View {
public func popup<PopupContent: View>(
isPresented: Binding<Bool>,
view: #escaping () -> PopupContent) -> some View {
self.modifier(
Popup(
isPresented: isPresented,
view: view)
)
}
}
public struct Popup<PopupContent>: ViewModifier where PopupContent: View {
init(isPresented: Binding<Bool>,
view: #escaping () -> PopupContent) {
self._isPresented = isPresented
self.view = view
}
/// Controls if the sheet should be presented or not
#Binding var isPresented: Bool
/// The content to present
var view: () -> PopupContent
}

Why is the navigation view bar so large in swiftui?

import SwiftUI
struct SecondView: View {
var ResearchMCQ: Question
//Creating Variables for Revision Topics
#State private var setOptionOne = false
#State private var setOptionTwo = false
#State private var setOptionThree = false
//User Home Page View
var body: some View {
//Allows for Navigation and Scrolling
NavigationView {
ScrollView{
//App Logo and Vertical Stacks
VStack(spacing: 1.0) {
Image("AppLogo")
.resizable()
.scaledToFit()
.padding(.trailing, 50.0)
.frame(height: 100, alignment: .topLeading)
Spacer()
Spacer()
//Multiple Choice Question Appears
Group {
Text(ResearchMCQ.question)
.padding(.trailing, 4)
Spacer()
Spacer()
//Ensures Only One Answer Can Be Selected
let OptionOne = Binding<Bool>(get: { self.setOptionOne }, set: { self.setOptionOne = $0; self.setOptionTwo = false; self.setOptionThree = false })
let OptionTwo = Binding<Bool>(get: { self.setOptionTwo }, set: { self.setOptionOne = false; self.setOptionTwo = $0; self.setOptionThree = false })
let OptionThree = Binding<Bool>(get: { self.setOptionThree }, set: { self.setOptionOne = false; self.setOptionTwo = false; self.setOptionThree = $0 })
//Shows User MCQ Options
VStack {
Toggle(ResearchMCQ.options[0], isOn: OptionOne)
.toggleStyle(.button)
.tint(Color(.gray))
.foregroundColor(Color("Black-White"))
Toggle(ResearchMCQ.options[1], isOn: OptionTwo)
.toggleStyle(.button)
.tint(Color(.gray))
.foregroundColor(Color("Black-White"))
Toggle(ResearchMCQ.options[2], isOn: OptionThree)
.toggleStyle(.button)
.tint(Color(.gray))
.foregroundColor(Color("Black-White"))
}
}
}
// .padding(.top, -150)
}
}
//Allows Navigation Through Pages
.navigationTitle("")
.padding(.top, -100)
}
}
Shows large navigation bar
I am trying to create a navigation bar, however I want it to be empty, hence the empty quotations mark in the code. Any idea on how I can make it smaller as when I scroll up as shown in the image the navigation bar takes up a good chunk of the page. As a first time swift coder, any help would be appreciated!!!
You just need one NavigationView. I guess you also have a NavigationView in your FirstView. And it is enough as this top NavigationView will contain all your deeper views.
So you don't need NavigationView {} in your SecondView. It is only needed in your FirstView. Just remove it from your SecondView and you will have a smaller Navigation bar.
Also .navigationTitle("") should be inside your NavigationView, and not as a modifier of itself :
struct FirstView: View {
var body: some View {
NavigationView {
NavigationLink("second View", destination: SecondView())
.navigationTitle("")
}
}
}
I have modified your code as I don't have the Question class/struct, but it should work the same with your code
struct SecondView: View {
var body: some View {
// I removed the NavigationView that was there
ScrollView{
VStack {
Text("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.")
Toggle("Toogle1", isOn: .constant(true))
Toggle("Toogle2", isOn: .constant(false))
Toggle("Toogle3", isOn: .constant(true))
}
}.navigationTitle("")
}
}
Result :

Why is my SwiftUI view not updating when the model changes

How do I update the content of an existing row in a List?
The print statement shows that the button is updating the Bool, but the View does not update .
the content (button) moves as expected, but the action and formating does not change as expected.
the code for the page I am using:
struct NotificationView: View {
#ObservedObject var notificationVModel: NotificationVModel = NotificationVModel()
var body: some View {
NavigationView{
List(notificationVModel.notificationarray,id:\.NotificationName){notificationVV in
ZStack {
if notificationVV.isShgowen {
Color (.green).opacity(0.1)
.cornerRadius(10)
}
HStack{
Button(action: {
notificationVV.changTogle()
print("\(notificationVV.isShgowen)")
}, label: {
ZStack{
Circle()
.foregroundColor(Color(red: 0 / 255, green: 175 / 255, blue: 80 / 255))
.frame(width: 50, height: 50, alignment: .center)
Image(systemName: "bell")
.frame(width: 40, height: 40, alignment: .center)
.foregroundColor(.white)
} })
VStack{
HStack {
if notificationVV.isShgowen{
Text("true")
.font(.custom("AvenirNext-DemiBold", size: 10))
}
Text(notificationVV.NotificationName)
.font(.custom("AvenirNext-DemiBold", size: 20))
Spacer()
Text(notificationVV.NotifivationDate)
.font(.custom("AvenirNext-Medium", size: 12))
.foregroundColor(.gray)
}.padding(.leading).padding(.trailing)
Text(notificationVV.NotificationDiscrip)
.font(.custom("AvenirNext-Regular", size: 11))
.lineLimit(nil)
.padding(.leading).padding(.trailing)
}
}.padding()
}
}
.navigationTitle("Notification")
.navigationBarTitleDisplayMode(.inline)
}
}
}
The ViewModel
class NotificationVModel: ObservableObject {
#Published var notificationarray : [NotificationV] = [
NotificationV(NotificationName: "Notification 1", NotificationDiscrip: "Lorem ipsum dolor sit amet,consec tetur adipiscing elit Lorem ipsum doloramet,consec tetur adipiscing elit sit ipi piscing… ", NotifivationDate: "04/02/2021", isShgowen: false), NotificationV(NotificationName: "Notification 2", NotificationDiscrip: "Lorem ipsum dolor sit amet,consec tetur adipiscing elit Lorem ipsum doloramet,consec tetur adipiscing elit sit ipi piscing… ", NotifivationDate: "05/03/2021", isShgowen: true),
]
}
The model
class NotificationV : ObservableObject{
let objectWillChange = ObservableObjectPublisher()
#Published var NotificationName : String = ""
#Published var NotificationDiscrip: String = ""
#Published var NotifivationDate:String = ""
#Published var isShgowen:Bool = false
init(NotificationName: String, NotificationDiscrip: String, NotifivationDate: String, isShgowen: Bool) {
self.NotificationName = NotificationName
self.NotificationDiscrip = NotificationDiscrip
self.NotifivationDate = NotifivationDate
self.isShgowen = isShgowen
}
func changTogle(){
if isShgowen == false {
isShgowen = true
}
}
}
Use struct rather than class for the NotificationV.
Create a new view for the buttons and send both your viewmodel and model to the new view.
Find index of model
Fore more info, read this tutorial
https://developer.apple.com/tutorials/swiftui/building-lists-and-navigation
struct NotificationView: View {
#ObservedObject var notificationVModel: NotificationVModel = NotificationVModel()
var body: some View {
NavigationView{
List(notificationVModel.notificationarray,id:\.NotificationName){notificationVV in
ZStack {
if notificationVV.isShowen {
Color (.green).opacity(0.1)
.cornerRadius(10)
}
HStack{
ToggleNotification(notifications: $notificationVModel.notificationarray, notificationV: notificationVV)
VStack{
HStack {
Text(notificationVV.isShowen ? "true": "false")
if notificationVV.isShowen {
Text("true")
.font(.custom("AvenirNext-DemiBold", size: 10))
}
Text(notificationVV.NotificationName)
.font(.custom("AvenirNext-DemiBold", size: 20))
Spacer()
Text(notificationVV.NotifivationDate)
.font(.custom("AvenirNext-Medium", size: 12))
.foregroundColor(.gray)
}.padding(.leading).padding(.trailing)
Text(notificationVV.NotificationDiscrip)
.font(.custom("AvenirNext-Regular", size: 11))
.lineLimit(nil)
.padding(.leading).padding(.trailing)
}
}.padding()
}
}
.navigationTitle("Notification")
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct ToggleNotification:View {
#Binding var notifications: [NotificationV]
var notificationV: NotificationV
var index:Int? {
notifications.firstIndex { $0.NotificationName == notificationV.NotificationName}
}
var body: some View {
Button(action: {
notifications[index!].isShowen.toggle()
print("\($notifications[index!].isShowen)")
}, label: {
ZStack{
Circle()
.foregroundColor(Color(red: 0 / 255, green: 175 / 255, blue: 80 / 255))
.frame(width: 50, height: 50, alignment: .center)
Image(systemName: "bell")
.frame(width: 40, height: 40, alignment: .center)
.foregroundColor(.white)
}
})
}
}
struct NotificationV {
var NotificationName : String = ""
var NotificationDiscrip: String = ""
var NotifivationDate:String = ""
var isShowen:Bool = false
init(NotificationName: String, NotificationDiscrip: String, NotifivationDate: String, isShowen: Bool) {
self.NotificationName = NotificationName
self.NotificationDiscrip = NotificationDiscrip
self.NotifivationDate = NotifivationDate
self.isShowen = isShowen
}
}
class NotificationVModel: ObservableObject {
#Published var notificationarray : [NotificationV] = [
NotificationV(NotificationName: "Notification 1", NotificationDiscrip: "Lorem ipsum dolor sit amet,consec tetur adipiscing elit Lorem ipsum doloramet,consec tetur adipiscing elit sit ipi piscing… ", NotifivationDate: "04/02/2021", isShowen: false), NotificationV(NotificationName: "Notification 2", NotificationDiscrip: "Lorem ipsum dolor sit amet,consec tetur adipiscing elit Lorem ipsum doloramet,consec tetur adipiscing elit sit ipi piscing… ", NotifivationDate: "05/03/2021", isShowen: true),
]
}
Follow case conventions. Names of types and protocols are UpperCamelCase. Everything else is lowerCamelCase.
https://swift.org/documentation/api-design-guidelines/
Update
As of now, NotificationV must unique NotificationName. Regardless of that, NotificationV should confirms to both Identifiable, Equatable protocols and use the following in ToggleNotification
var index:Int? {
notifications.firstIndex { $0 == notificationV}
}
NotificationV
struct NotificationV: Identifiable, Equatable {
var id: UUID = UUID()
var NotificationName : String = ""
var NotificationDiscrip: String = ""
var NotifivationDate:String = ""
var isShowen:Bool = false
init(NotificationName: String, NotificationDiscrip: String, NotifivationDate: String, isShowen: Bool) {
self.NotificationName = NotificationName
self.NotificationDiscrip = NotificationDiscrip
self.NotifivationDate = NotifivationDate
self.isShowen = isShowen
}
}
Doing so you always find correct NotificationV even if they do not have unique name.

Why My second view cannot jump back to the root view properly

My App currently has two pages, first page has a circle plus button which could lead us to a second page. Basically, I have a save button which after clicking it, we could get back to the rood page. I followed this link for going back to root view. I tried the most up voted code, his code works perfectly. I reduced his code to two scene (basically the same scenario as mine), which also works perfectly. But then I don't know why my own code, pasted below, doesn't work. Basically my way of handling going back to root view is the same as the one in the link.
//
// ContentView.swift
// refridgerator_app
//
// Created by Mingtao Sun on 12/22/20.
//
import SwiftUI
import UIKit
#if canImport(UIKit)
extension View {
func hideKeyboard() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
#endif
struct ContentView: View {
#EnvironmentObject private var fridge : Fridge
private var dbStartWith=0;
#State var pushed: Bool = false
#State private var selection = 1;
#State private var addFood = false;
var body: some View {
TabView(selection: $selection) {
NavigationView {
List(fridge.container!){
food in NavigationLink(destination: FoodView()) {
Text("HI")
}
}.navigationBarTitle(Text("Fridge Items"), displayMode: .inline)
.navigationBarItems(trailing:
NavigationLink(destination: AddFoodView(pushed: self.$pushed),isActive: self.$pushed) {
Image(systemName: "plus.circle").resizable().frame(width: 22, height: 22)
}.isDetailLink(false) )
}
.tabItem {
Image(systemName: "house.fill")
Text("Home")
}
.tag(1)
Text("random tab")
.font(.system(size: 30, weight: .bold, design: .rounded))
.tabItem {
Image(systemName: "bookmark.circle.fill")
Text("profile")
}
.tag(0)
}.environmentObject(fridge)
}
}
struct FoodView: View{
var body: some View{
NavigationView{
Text("food destination view ");
}
}
}
struct AddFoodView: View{
#Binding var pushed : Bool
#EnvironmentObject private var fridgeView : Fridge
#State private var name = ""
#State private var count : Int = 1
#State private var category : String = "肉类";
#State var showCategory = false
#State var showCount = false
var someNumberProxy: Binding<String> {
Binding<String>(
get: { String(format: "%d", Int(self.count)) },
set: {
if let value = NumberFormatter().number(from: $0) {
self.count = value.intValue;
}
}
)
}
var body: some View{
ZStack{
NavigationView{
VStack{
Button (action: {
self.pushed = false ;
//let tempFood=Food(id: fridgeView.index!,name: name, count: count, category: category);
//fridgeView.addFood(food: tempFood);
} ){
Text("save").foregroundColor(Color.blue).font(.system(size: 18,design: .default)) }
}.navigationBarTitle("Three")
}
ZStack{
if self.showCount{
Rectangle().fill(Color.gray)
.opacity(0.5)
VStack(){
Spacer(minLength: 0);
HStack{
Spacer()
Button(action: {
self.showCount=false;
}, label: {
Text("Done")
}).frame(alignment: .trailing).offset(x:-15,y:15)
}
Picker(selection: $count,label: EmptyView()) {
ForEach(1..<100){ number in
Text("\(number)").tag("\(number)")
}
}.labelsHidden()
} .frame(minWidth: 300, idealWidth: 300, maxWidth: 300, minHeight: 250, idealHeight: 100, maxHeight: 250, alignment: .top).fixedSize(horizontal: true, vertical: true)
.background(RoundedRectangle(cornerRadius: 27).fill(Color.white.opacity(1)))
.overlay(RoundedRectangle(cornerRadius: 27).stroke(Color.black, lineWidth: 1))
.offset(x:10,y:-10)
Spacer()
}
if self.showCategory{
let categoryArr = ["肉类","蔬菜类","饮料类","调味品类"]
ZStack{
Rectangle().fill(Color.gray)
.opacity(0.5)
VStack(){
Spacer(minLength: 0);
HStack{
Spacer()
Button(action: {
self.showCategory=false;
}, label: {
Text("Done")
}).frame(alignment: .trailing).offset(x:-15,y:15)
}
Picker(selection: $category,label: EmptyView()) {
ForEach(0..<categoryArr.count){ number in
Text(categoryArr[number]).tag(categoryArr[number])
}
}.labelsHidden()
} .frame(minWidth: 300, idealWidth: 300, maxWidth: 300, minHeight: 250, idealHeight: 100, maxHeight: 250, alignment: .top).fixedSize(horizontal: true, vertical: true)
.background(RoundedRectangle(cornerRadius: 27).fill(Color.white.opacity(1)))
.overlay(RoundedRectangle(cornerRadius: 27).stroke(Color.black, lineWidth: 1))
Spacer()
}.offset(x:10,y:20)
}
}
}.animation(.easeInOut)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
If you read my code carefully, there are some variables are missing referencing. That's because I pasted part of the code that relates to my issue.
Food Class
//
// Food.swift
// refridgerator_app
//
// Created by Mingtao Sun on 12/23/20.
//
import Foundation
class Food: Identifiable {
init(id:Int, name: String, count: Int, category: String){
self.id=id;
self.name=name;
self.count=count;
self.category=category;
}
var id: Int
var name: String
var count: Int
var category: String
}
Fridge class
//
// Fridge.swift
// refridgerator_app
//
// Created by Mingtao Sun on 12/27/20.
//
import Foundation
class Fridge: ObservableObject{
init(){
db=DBhelper();
let result = setIndex(database: db!);
self.index = result.1;
self.container=result.0;
}
var db:DBhelper?
var index : Int?
#Published var container : [Food]?;
func setIndex(database: DBhelper) -> ([Food],Int){
let foodList : [Food] = database.read();
var index=0;
for food in foodList{
index = max(food.id,index);
}
return (foodList,(index+1));
}
func updateindex(index: inout Int){
index=index+1;
}
func testExist(){
if let data = db {
print("hi")
}
else{
print("doesnt exist")
}
}
func addFood(food:Food){
self.db!.insert(id: self.index!, name: food.name, count:food.count, category: food.category);
self.container!.append(food);
}
}
Because you implemented a new NaviagtionView in AddFoodView. Simply remove this and it should work. Look at the link you provided. There is no NavigationView in the child.
Correct me if Im wrong but the core code parts here that produce this issue are as follows:
Here you start:
struct ContentView: View {
#State var pushed: Bool = false
// Deleted other vars
var body: some View {
TabView(selection: $selection) {
NavigationView {
List(fridge.container!){
food in NavigationLink(destination: FoodView()) {
Text("HI")
}
}.navigationBarTitle(Text("Fridge Items"), displayMode: .inline)
.navigationBarItems(trailing:
// Here you navigate to the child view
NavigationLink(destination: AddFoodView(pushed: self.$pushed),isActive: self.$pushed) {
Image(systemName: "plus.circle").resizable().frame(width: 22, height: 22)
}.isDetailLink(false) )
}
Here you land and want to go back to root:
struct AddFoodView: View{
#Binding var pushed : Bool
// Deleted the other vars for better view
var body: some View{
ZStack{
NavigationView{ // <-- remove this
VStack{
Button (action: {
// here you'd like to go back
self.pushed = false;
} ){
Text("save").foregroundColor(Color.blue).font(.system(size: 18,design: .default)) }
}.navigationBarTitle("Three")
}
For the future:
I have the feeling you might have troubles with the navigation in general.
Actually it is really simple:
You implement one NavigationView at the "root" / start of your navigation.
From there on you only use NavigationLinks to go further down to child pages. No NavigationView needed anymore.

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.