How can I use accessibilityScrollAction in SwiftUI? - scroll

How can I use accessibilityScrollAction? Can I deactivate the swipe function in a form or list?
import SwiftUI
struct Vorschau: View {
#State var pickOn = 0
#State var PickedName : String = ""
var body: some View {
Form{
Picker("MaPicker", selection: $pickOn) {
ForEach(0 ..< 5) { x in
Text("\(x)").tag(x)
}
}.accessibilityScrollAction(<#T##handler: (Edge) -> Void##(Edge) -> Void#>)
}
}
}
#if DEBUG
struct Vorschau_Previews: PreviewProvider {
static var previews: some View {
Vorschau()
}
}
#endif

Related

Why won't this simple #State (SwiftUI on MacOS) example update the View?

Xcode 14.1 (14B47b), Ventura 13.0.1, Swift 5
When clicking the button, it prints consecutive numbers in the debug window, but the SwiftUI View does not update. I couldn't get it to work on a much more complicated app and ran into this problem. Then I reduced it to this test project, and it still dosn't work.
This should be a trivial use of #State.
This is for a SwiftUI app running on MacOS.
What am I doing wrong (other than losing my mind)?
import SwiftUI
var globalCounter:Int = 0
#main
struct State_TestApp: App {
init() {
globalCounter = 1
}
var body: some Scene {
WindowGroup {
ContentView(counter:globalCounter)
}
}
}
func addOne() {
globalCounter += 1
print(globalCounter)
}
struct ContentView: View {
#State var counter:Int
var body: some View {
VStack {
Button("Add one") {
addOne()
}
Text("\(counter)")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(counter:globalCounter)
}
}
Here is the answer for those that want to do something similar.
#main
struct State_TestApp: App {
var body: some Scene {
WindowGroup {
ContentView(start:3)
}
}
}
func addOne(number:Int) -> Int {
return number + 1
}
struct ContentView: View {
init(start:Int) {
counter = start
}
#State private var counter:Int
var body: some View {
VStack {
Button("Add one") {
counter = addOne(number: counter)
}
Text("\(counter)")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(start:4)
}
}

The picker doesn't update the number of segments it has when a value is changed during execution

I have recently started learning swiftUI and I'm facing some issues here. This is the code:
struct ContentView: View {
#State private var measurementType = 0
#State private var inputValue = ""
#State private var inputUnit = 0
#State private var outputUnit = 1
var outputValue = ""
let measurementTypes = ["Temp", "Length", "Time", "Volume"]
var typeDictionary = [
["Celsius", "Fahrenheit", "Kelvin"],
["Meters", "Kilometers", "Feet", "Yards", "Miles"],
["Seconds", "Minutes", "Hours", "Days"],
["Milliliters", "Liters", "Cups", "Pints", "Gallons"]
]
var body: some View {
NavigationView {
Form {
Section(header: Text("Choose the type of measurement")) {
Picker("The type of measurement", selection: $measurementType) {
ForEach(0 ..< measurementTypes.count) {
Text("\(measurementTypes[$0])")
}
}
.id(measurementType)
.pickerStyle(SegmentedPickerStyle())
}
Section {
TextField("Enter the value", text: $inputValue)
.keyboardType(.decimalPad)
}
Section(header: Text("Choose the input unit")) {
Picker("The input unit", selection: self.$inputUnit) {
ForEach(0 ..< typeDictionary[measurementType].count) {
Text("\(typeDictionary[measurementType][$0])")
}
}
.pickerStyle(SegmentedPickerStyle())
}
Section(header: Text("Choose the output unit")) {
}
Section(header: Text("Converted Value")) {
Text("")
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Whenever I change the measurementType while the code is running, the values of the picker in the input units section update, but the number of segments don't. If I start with the value of measurementType as 1, then the app simply crashes when I choose Temp or Time from the picke.
When using ForEach, you need to provide an id so SwiftUI can uniquely identify each element in the array and know when to redraw the view. You can use id: \.self to use the item's value as its id.
Here's an updated working version:
import SwiftUI
struct ContentView: View {
#State private var measurementType = 0
#State private var inputValue = ""
#State private var inputUnit = 0
#State private var outputUnit = 1
var outputValue = ""
let measurementTypes = ["Temp", "Length", "Time", "Volume"]
var typeDictionary = [
["Celsius", "Fahrenheit", "Kelvin"],
["Meters", "Kilometers", "Feet", "Yards", "Miles"],
["Seconds", "Minutes", "Hours", "Days"],
["Milliliters", "Liters", "Cups", "Pints", "Gallons"]
]
var body: some View {
NavigationView {
Form {
Section(header: Text("Choose the type of measurement")) {
Picker("The type of measurement", selection: $measurementType) {
ForEach(0 ..< measurementTypes.count, id: \.self) {
Text("\(measurementTypes[$0])")
}
}
.id(measurementType)
.pickerStyle(SegmentedPickerStyle())
}
Section {
TextField("Enter the value", text: $inputValue)
.keyboardType(.decimalPad)
}
Section(header: Text("Choose the input unit")) {
Picker("The input unit", selection: self.$inputUnit) {
ForEach(0 ..< typeDictionary[measurementType].count, id: \.self) {
Text("\(typeDictionary[measurementType][$0])")
}
}
.pickerStyle(SegmentedPickerStyle())
}
Section(header: Text("Choose the output unit")) {
}
Section(header: Text("Converted Value")) {
Text("")
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Send data to next view - SwiftUI

So I have been trying to get a simple pass of data working with SwiftUI.
Basically the below script prints out a list of items (in a HStack) I then have each one linked to our Podcast() view.
What I am trying to do is pass through the podcast name to the next view. How do I achieve
this? As all the examples are about int which I am not using am using a String.
import SwiftUI
import RemoteImage
struct ContentView: View {
#State private var showAlert = false
#State var posts: [Program] = []
var body: some View {
NavigationView {
if posts.isEmpty {
Text("Loading")
} else {
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .bottom, spacing: 10) {
ForEach(posts) { post in
//return
NavigationLink(destination: Podcasts()){
RemoteImage(type: .url(URL(string:post.icon)!), errorView: { error in
Text(error.localizedDescription)
}, imageView: { image in
image
.resizable()
.renderingMode(.original)
/* .clipShape(Circle())
.shadow(radius: 10)
.overlay(Circle().stroke(Color.red, lineWidth: 5))*/
.aspectRatio(contentMode: .fit)
.frame(width:200, height:200)
}, loadingView: {
Text("Loading ...")
})
}
}
}.frame(height: 200)
}.frame(height: 200)
}
}.onAppear {
Api().getPosts { (posts) in
self.posts = posts
}
}.navigationBarTitle(Text("Home"))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The podcast View
import SwiftUI
struct Podcasts: View {
var body: some View {
NavigationView{
Text("Hello")
}.navigationBarTitle(Text("Podcast"))
}
}
struct Podcasts_Previews: PreviewProvider {
static var previews: some View {
Podcasts()
}
}
Pass post as constructor argument, like
ForEach(posts) { post in
//return
NavigationLink(destination: Podcasts(post: post)){
so now
struct Podcasts: View {
let post: Program
var body: some View {
// !! DON'T ADD SECOND NAVIGATION VIEW IN STACK
// !! - THERE MUST BE ONLY ONE
Text(post.name)
.navigationBarTitle(Text("Podcast"))
}
}

How to access value from an item in ForEach list

How to access values from particular item on the list made with ForEach?
As you can see I was trying something like this (and many other options):
Text(item[2].onOff ? "On" : "Off")
I wanted to check the value of toggle of 2nd list item (for example) and update text on the screen saying if it's on or off.
And I know that it's something to do with #Binding and I was searching examples of this and trying few things, but I cannot make it to work. Maybe it is a beginner question. I would appreciate if someone could help me.
My ContentView:
struct ContentView: View {
// #Binding var onOff : Bool
#State private var onOff = false
#State private var test = false
var body: some View {
NavigationView {
List {
HStack {
Text("Is 2nd item on or off? ")
Text(onOff ? "On" : "Off")
// Text(item[2].onOff ? "On" : "Off")
}
ForEach((1...15), id: \.self) {item in
ListItemView()
}
}
.navigationBarTitle(Text("List"))
}
}
}
And ListItemView:
import SwiftUI
struct ListItemView: View {
#State private var onOff : Bool = false
// #Binding var onOff : Bool
var body: some View {
HStack {
Text("99")
.font(.title)
Text("List item")
Spacer()
Toggle(isOn: self.$onOff) {
Text("Label")
}
.labelsHidden()
}
}
}
I don't know what exactly you would like to achieve, but I made you a working example:
struct ListItemView: View {
#ObservedObject var model: ListItemModel
var body: some View {
HStack {
Text("99")
.font(.title)
Text("List item")
Spacer()
Toggle(isOn: self.$model.switchedOnOff) {
Text("Label")
}
.labelsHidden()
}
}
}
class ListItemModel: ObservableObject {
#Published var switchedOnOff: Bool = false
}
struct ContentView: View {
#State private var onOff = false
#State private var test = false
#State private var list = [
(id: 0, model: ListItemModel()),
(id: 1, model: ListItemModel()),
(id: 2, model: ListItemModel()),
(id: 3, model: ListItemModel()),
(id: 4, model: ListItemModel())
]
var body: some View {
NavigationView {
List {
HStack {
Text("Is 2nd item on or off? ")
Text(onOff ? "On" : "Off")
// Text(item[2].onOff ? "On" : "Off")
}
ForEach(self.list, id: \.id) {item in
ListItemView(model: item.model)
}
}
.navigationBarTitle(Text("List"))
}.onReceive(self.list[1].model.$switchedOnOff, perform: { switchedOnOff_second_item in
self.onOff = switchedOnOff_second_item
})
}
}
The #Published basically creates a Publisher, which the UI can listen to per onReceive().
Play around with this and you will figure out what these things do!
Good luck :)
import SwiftUI
struct ContentView: View {
#State private var onOffList = Array(repeating: true, count: 15)
var body: some View {
NavigationView {
List {
HStack {
Text("Is 2nd item on or off? ")
Text(onOffList[1] ? "On" : "Off")
}
ForEach((onOffList.indices), id: \.self) {idx in
ListItemView(onOff: self.$onOffList[idx])
}
}
.navigationBarTitle(Text("List"))
}
}
}
struct ListItemView: View {
#Binding var onOff : Bool
var body: some View {
HStack {
Text("99")
.font(.title)
Text("List item")
Spacer()
Toggle(isOn: $onOff) {
Text("Label")
}
.labelsHidden()
}
}
}
I understand that you are directing me to use ObservableObject. And probably it's the best way to go with final product. But I am still thinking about #Binding as I just need to pass values better between 2 views only. Maybe I still don't understand binding, but I came to this solution.
struct ContentView: View {
// #Binding var onOff : Bool
#State private var onOff = false
// #State private var test = false
var body: some View {
NavigationView {
List {
HStack {
Text("Is 2nd item on or off? ")
Text(onOff ? "On" : "Off")
// Text(self.item[2].$onOff ? "On" : "Off")
// Text(item[2].onOff ? "On" : "Off")
}
ForEach((1...15), id: \.self) {item in
ListItemView(onOff: self.$onOff)
}
}
.navigationBarTitle(Text("List"))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and ListItemView:
import SwiftUI
struct ListItemView: View {
// #State private var onOff : Bool = false
#Binding var onOff : Bool
var body: some View {
HStack {
Text("99")
.font(.title)
Text("List item")
Spacer()
Toggle(isOn: self.$onOff) {
Text("Label")
}
.labelsHidden()
}
}
}
What is happening now is text is being updated after I tap toggle. But I have 2 problems:
tapping 1 toggle changes all of them. I think it's because of this line:
ListItemView(onOff: self.$onOff)
I still cannot access value of just one row. In my understanding ForEach((1...15), id: .self) make each row have their own id, but I don't know how to access it later on.

Change State with DocumentPicker SwiftUI

I'm trying to get a List View to appear after selecting a document with documentPicker. Getting the following error...
Fatal error: No ObservableObject of type Switcher found.
A View.environmentObject(_:) for Switcher may be missing as an ancestor of this view.: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/Monoceros/Monoceros-30.4/Core/EnvironmentObject.swift, line 55
It seems like I should use an EnviromentObject binding to have all views be able to read, access and update the Switcher class. Under the Coordinator Class in CSVDocumentPicker.swift is where things seem to go wrong.
I'm using #EnvironmentObject var switcher:Switcher and using the documentPicker function to toggle the switcher state so the Lists View will be displayed. I'm stumped.
SceneDelegate.swift
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
var switcher = Switcher()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = ContentView().environmentObject(Switcher())
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(switcher))
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
}
func sceneDidBecomeActive(_ scene: UIScene) {
}
func sceneWillResignActive(_ scene: UIScene) {
}
func sceneWillEnterForeground(_ scene: UIScene) {
}
func sceneDidEnterBackground(_ scene: UIScene) {
}
}
CSVDocumentPicker.swift
import Combine
import SwiftUI
class Switcher: ObservableObject {
var didChange = PassthroughSubject<Bool, Never>()
var isEnabled = false {
didSet {
didChange.send(self.isEnabled)
}
}
}
struct CSVDocumentPicker: View {
#EnvironmentObject var switcher:Switcher
#State private var isPresented = false
var body: some View {
VStack{
Text("csvSearch")
Button(action: {self.isPresented = true
})
{Text("import")
Image(systemName: "folder").scaledToFit()
}.sheet(isPresented: $isPresented) {
() -> DocumentPickerViewController in
DocumentPickerViewController.init(onDismiss: {
self.isPresented = false
})
}
if switcher.isEnabled {
ListView()
}
}
}
}
struct CSVDocumentPicker_Previews: PreviewProvider {
static var previews: some View {
CSVDocumentPicker().environmentObject(Switcher())
}
}
/// Wrapper around the `UIDocumentPickerViewController`.
struct DocumentPickerViewController {
private let supportedTypes: [String] = ["public.item"]
// Callback to be executed when users close the document picker.
private let onDismiss: () -> Void
init(onDismiss: #escaping () -> Void) {
self.onDismiss = onDismiss
}
}
// MARK: - UIViewControllerRepresentable
extension DocumentPickerViewController: UIViewControllerRepresentable {
typealias UIViewControllerType = UIDocumentPickerViewController
func makeUIViewController(context: Context) -> DocumentPickerViewController.UIViewControllerType {
let documentPickerController = UIDocumentPickerViewController(documentTypes: supportedTypes, in: .import)
documentPickerController.allowsMultipleSelection = false
documentPickerController.delegate = context.coordinator
return documentPickerController
}
func updateUIViewController(_ uiViewController: DocumentPickerViewController.UIViewControllerType, context: Context) {}
// MARK: Coordinator
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UIDocumentPickerDelegate, ObservableObject {
#EnvironmentObject var switcher:Switcher
var parent: DocumentPickerViewController
init(_ documentPickerController: DocumentPickerViewController) {
parent = documentPickerController
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) {
globalPathToCsv = url
loadCSV()
switcher.isEnabled.toggle()
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
parent.onDismiss()
}
}
}
ContentView.swift
import SwiftUI
import UIKit
var globalPathToCsv:URL!
var csvArray = [[String:String]]()
var csv = CSVaccessability()
func loadCSV(){
csv.csvToList()
// print(csvArray)
}
struct ContentView: View {
#EnvironmentObject var switcher:Switcher
var body: some View {
VStack{
CSVDocumentPicker().environmentObject(Switcher())
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(Switcher())
}
}
ListView.swift
import SwiftUI
struct ListView: View {
var body: some View {
HStack{
List {
ForEach(csvArray, id:\.self) { dict in Section {DataList(dict: dict)} }
}
}}
}
struct DataList : View {
#State var dict = [String: String]()
var body: some View {
let keys = dict.map{$0.key}
let values = dict.map {$0.value}
return ForEach(keys.indices) {index in
HStack {
Text(keys[index])
Text("\(values[index])")
}
}
}
}
struct ListView_Previews: PreviewProvider {
static var previews: some View {
ListView()
}
}
CSVaccessability.swift
import Foundation
import SwiftCSV
var csvData:[[String]]!
var headers:[String] = []
class CSVaccessability {
var numberOfColumns:Int!
var masterList = [[String:Any]]()
func csvToList(){
if let url = globalPathToCsv {
do {
print(url)
let csvFile: CSV = try CSV(url: globalPathToCsv)
let csv = csvFile
//print(stream)
//print(csvFile)
headers = csv.header
csvArray=csv.namedRows
} catch {print("contents could not be loaded")}}
else {print("the URL was bad!")}
}
}
I've imported SwiftCSV for this project...
Created a new class...
ToggleView.swift
import Foundation
class ToggleView: ObservableObject {
#Published var toggleView: Bool = false
}
Added the Environment Object to ContentView.swift
#EnvironmentObject var viewToggle: ToggleView
Also added .environmentObject(ToggleView()) to any view that would be called and cause a crash the crash logs helped with this...
Text("csvSearch")
Button(action: {self.isPresented = true
self.viewToggle.toggleView.toggle()
// self.switcher = true
})
{Text("import")
Image(systemName: "folder").scaledToFit()
}.sheet(isPresented: $isPresented) {
() -> DocumentPickerViewController in
DocumentPickerViewController.init()
}
if self.picker {
DocumentPickerViewController().environmentObject(ToggleView())
}
if self.viewToggle.toggleView{
ListView()
}
}
}
}
Did you ever get this working? The only problem I found was with the line var csv = CSVaccessability() in the ContentView. CSVaccessability does not exist.
This is my solution for the Catalyst app for Mac, but to avoid pressing theImage (systemName: "book")button a second time to update the data in the text fields, I have implemented a hidden view in GeoFolderReadFileView to force the view update.
//File: GeoFolderCodStruct
import Foundation
struct GeoFolderCodStruct:Codable {
var isActive:Bool = true
var dataCreazione:Date = Date()
var geoFolderPath:String = ""
var nomeCartella:String = ""
var nomeCommittente:String = ""
var nomeArchivio:String = ""
var note:String = ""
var latitudine:Double? = nil
var longitudine:Double? = nil
var radiusCircle:Int16? = nil
//Roma 42.1234 13.1234
}
//File: PickerForReadFile
import SwiftUI
final class PickerForReadFile: NSObject, UIViewControllerRepresentable, ObservableObject {
#Published var geoFolder = GeoFolderCodStruct()
lazy var viewController:UIDocumentPickerViewController = {
let vc = UIDocumentPickerViewController(documentTypes: ["geof"], in: .open)
vc.allowsMultipleSelection = false
vc.delegate = self
return vc
}()
func makeUIViewController(context: UIViewControllerRepresentableContext<PickerForReadFile>) -> UIDocumentPickerViewController {
viewController.delegate = self
return viewController
}
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<PickerForReadFile>) {
print("updateUIViewController")
}
}
extension PickerForReadFile: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
if urls.count > 0 {
DispatchQueue.main.async {
let url = urls[0]
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let jsonData = try decoder.decode(GeoFolderCodStruct.self, from: data)
self.geoFolder = jsonData
print("geoFolder: \(self.geoFolder.nomeArchivio)")
} catch {
print("error:\(error)")
}
}
}
}
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
controller.dismiss(animated: true) {
print("Cancel from picker view controller")
}
}
}
//File: GeoFolderReadFileView
import SwiftUI
struct GeoFolderReadFileView: View {
#ObservedObject var picker = PickerForReadFile()
#Binding var geoFolder: GeoFolderCodStruct
var body: some View {
VStack(alignment: .trailing){
Button(action: {
#if targetEnvironment(macCatalyst)
print("Press open file")
let vc = UIApplication.shared.windows[0].rootViewController!
vc.present(self.picker.viewController, animated: true) {
self.geoFolder = self.picker.geoFolder
}
#endif
}) {
Image(systemName: "book")
}
urlPickedView()
.hidden()
}
}
private func urlPickedView() -> some View {
DispatchQueue.main.async {
let geoF = self.picker.geoFolder
print("Committente: \(geoF.nomeCommittente) - Archivio: \(geoF.nomeArchivio)")
self.geoFolder = geoF
}
return TextField("", text: $geoFolder.geoFolderPath)
}
}
//File: ContentView
import SwiftUI
struct ContentView: View {
#State private var geoFolder = GeoFolderCodStruct()
var body: some View {
VStack {
HStack {
Text("Open GeoFolder File:")
.padding()
Spacer()
GeoFolderReadFileView(geoFolder: $geoFolder)
.padding()
}
.padding()
Group {
TextField("", text: $geoFolder.geoFolderPath)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
TextField("", text: $geoFolder.nomeCartella)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
TextField("", text: $geoFolder.nomeCommittente)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
TextField("", text: $geoFolder.nomeArchivio)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
TextField("", text: $geoFolder.note)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
.padding()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and the last, the json file to read for test the code.
{
"nomeCommittente" : "Appple",
"note" : "Image from Cupertino (CA).",
"latitudine" : 37.332161,
"longitudine" : -122.030352,
"nomeCartella" : "Foto",
"geoFolderPath" : "\/Users\/cesare\/Desktop",
"radiusCircle" : 50,
"dataCreazione" : "20\/03\/2020",
"nomeArchivio" : "AppleCampus-Image",
"isActive" : true
}
I was unable to implement the solution proposed by #Mdoyle1. I hope someone can edit the code to make it work as it should, without create hidden view.

Resources