Autor-Archiv Tobias Stephan

VonTobias Stephan

SwiftUI toggle action events

Der Toggle Switch in SwiftUI hat leider keinen „action“ event, wie der Button. Wenn man in die geschweiften Klammern einen Funktionsaufruf einträgt, für das zu folgender Fehlermeldung:

Static method ‚buildBlock‘ requires that ‚String‘ conform to ‚View‘

Der Trick ist, dass man hier ein View erzeugen muss.

Im Beispielcode findest Du die Zeile:

Text("Toggle \(ToggleAction(State: showResubmission))")


Das ist Lösung. Nimm die Funktion ToggleAction und baue sie für Deine Zwecke aus. In diesem Beispiel ist einfach nur die Statusvariable als Rückgabewert eingetragen.

//
//  ContentView.swift
//  ToggleSwitchActionDemo
//
//  Created by T. Stephan on 22.02.20.
//  Copyright © 2020 eCommerce - Tobias Stephan. All rights reserved.
//

import SwiftUI
import Combine

class MainClass: ObservableObject {
   public var ToggleSwitchState = false

    init(){

    }
}

struct ContentView: View {
    @ObservedObject var oMainClass = MainClass()
       @State private var showResubmission = false
    var body: some View {
        Toggle(isOn: $showResubmission){
            Text("Toggle \(ToggleAction(State: showResubmission))")
            .padding()
        }
    }

    func ToggleAction(State: Bool) -> String {
        if (State != oMainClass.ToggleSwitchState)
        {
            oMainClass.ToggleSwitchState = State
            //do something else...
            print("Toggle switched...")
        }
        return String(State)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Download SwiftUI Toggle Action Event
Downlod SwiftUI Toggle Action Demo Project
VonTobias Stephan

XCode delete breakpoints löschen

Wenn Du Deine Breakpoints entfernen möchtest, findest Du links oben das Breakpoint Symbol hier erhältst Du einen guten Überblick über alle gesetzten Breakpoints. Diese können einzeln oder alle auf einmal entfernt werden.

Delete XCode debug Breakpoints
VonTobias Stephan

Swift Datum vergleich compare dates

Die Datumsstruktur von Swift entspricht sowohl der Gleichheit als auch der Vergleichbarkeit, d.h. Du überprüfst zwei Daten auf Gleichheit und vergleichen sie, um zu sehen, welches früher ist, also in der Vergangenheit liegt.

In der Praxis bedeutet dies, dass Du <, > und == verwendest, um zu vergleichen. Genau so, wie Du es mit Zeichenketten und ganzen Zahlen umgingest. Probier es einfach mal aus:

let now = Date()
let soon = Date().addingTimeInterval(5000)

now == soon
now != soon
now < soon
now > soon
VonTobias Stephan

SwiftUI close view dismiss schließen

Wenn Du eine SwiftUI-Ansicht mit Hilfe eines Blattes anzeigst, ist es üblich, dass Du diese View schliessen möchtest , wenn etwas passiert – wenn der Benutzer z.B. auf eine Schaltfläche tippt. Es gibt zwei Möglichkeiten, dies in Swift zu lösen, und ich werde Dir beide zeigen, damit Du entscheiden kannst, welche Deinen Bedürfnissen entspricht.

Die erste Möglichkeit besteht darin, der Ansicht zu sagen, dass sie sich selbst verwerfen soll, indem sie ihren Darstellungsstatus ändert. Dazu fügen Sie zunächst eine neue Umgebungseigenschaft zu der Ansicht hinzu, die Du schliessen möchtest:

@Environment(\.presentationMode) var presentationMode

Mit folgender Zeile schliesst Du dann schlussendlich die View:

self.presentationMode.wrappedValue.dismiss()

Die andere Möglichkeit besteht darin, eine Bindung in die gezeigte View zu übergeben, so dass der Wert der Bindung wieder auf „false“ geändert werden kann.

Wenn Du z.B. ViewA mit einer @State-Eigenschaft wie dieser hatten:

@State var showingNewUserView = false

Du kannst Deine NewUserView mit einem Sheet wie diesem präsentieren:

.sheet(isPresented: $showingNewUserView) {
    NewUserView()
}

Bei der Verwendung des Bindungsansatzes für das Verwerfen einer View würden Du Deine Zielansicht – die als Sheet angezeigt wurde – so modifizieren, dass sie eine Bindung desselben Typs hat, der für die Präsentation der View verwendet wurde.

In unserem obigen Beispiel haben wir das Sheet präsentiert, als die View von NewUserView den Wert true hatte, also müssten wir eine boolesche Bindung zu NewUserView hinzufügen, wie hier:

@Binding var isPresented: Bool

Wenn Du möchtest, dass NewUserView sich selbst beendet, setze einfach isPresent zurück auf false, so wie hier:

self.isPresented = false

Nun zum wichtigen Teil: Wenn Du Deine Zielansicht (in diesem Beispiel NewUserView) erstellst, musst Du diesen Datenfluss als Teil Deines Initialisierers übergeben, damit Swift weiß, mit welchem Booleschen es arbeitet.

Du könntest also Deinen bisherigen Sheet-Code durch diesen ersetzen:

.sheet(isPresented: $showingNewUserView) {
    NewUserView(isPresented: self.$showingNewUserView)
}

Bei diesem Ansatz veranlasst SwiftUI diese beiden Ansichten, denselben booleschen Wert zu lesen und zu schreiben, d.h. wenn NewUserView den booleschen Wert wieder auf „false“ setzt, wird das Blatt automatisch ausgeblendet.

VonTobias Stephan

SwiftUI list refresh does not work funktioniert nicht

Ich hatte das Problem, dass sich die Liste schlichtweg nicht aktualisiert hat. Alle Versuche die Liste dazu zu bringen, sich zu verändern, wenn sich ein Wert des Arrays ändert, führte leider nicht zum Erfolg. Auch ein kompletter Reload des Arrays war nicht zielführend. Für die SwiftUI List habe ich diese Klasse ListDataSource als Observerable Object eingerichtet. Eigentlich hätte sich die Liste bei Änderung eines Wertes im Array ListOfListEntries automatisch alles ändern müssen, also ein Refresh hätte stattfinden sollen. Dem war aber nicht so. Nach langem Suchen war es nur eine Kleinigkeit – nämlich das @Published hat gefehlt. Ich hoffe denjenigen damit geholfen zu haben, die sich damit auch gerade die Finger „brechen“.

class ListDataSource: ObservableObject {
    //Nicht mehr erforderlich?
    //let didChange = PassthroughSubject()
    let didChange = PassthroughSubject()



   @Published var ListOfListEntries = [oListEntryFields]() //{
        //didSet { didChange.send() }

    //}
    var ListOfGroups = [oListGroups]()
    let oListoryParser = ListoryXMLParser()
    let defaults = UserDefaults.standard

    var ActiveListKey : String = ""
    var ActiveGroupKey : String = ""
    var ActiveListTitle : String = ""
    var ActiveGroupTitle : String = ""

    init() {
        reload()
        didChange.send()
    }
}
VonTobias Stephan

SwiftUI Textfield Textbox füllen

…das ist nun wirklich keine Raketenwissenschaft. Den aktuellen Wert hinterlegt man as String – direkte Texteingabe oder halt eine Variable. Die Eigenschaft Text findest Du dann der @State Variable $name wieder. Mit Hilfe der textfieldStyle Eigenschaft RoundedBorderTextFieldStyle() sieht die Textbox so aus, wie wir das aus den Storyboard-Zeiten kennen. Natürlich gibt es zahlreiche Möglichkeiten das Erscheinungsbild des TextField Objekts wunschgemäß anzupassen.

struct ContentView: View {
    @State private var name: String = "Peter"

    var body: some View {
        VStack {
            TextField("Enter your name", text: $name)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            Text("Hello, \(name)!")
        }
    }
}

VonTobias Stephan

Swift Datum in string konvertieren Date to String Convert DE

Hier eine einfache Funktion mit der Du ein Date in ein deutsches Datumsformat als Stirng konvertieren kannst. Das in deutschland übliche Format sieht so aus: „dd.MM.yyyy hh:mm:ss“

Natürlich kannst Du das auch durch andere Formate prolbemlos ersetzen.

public static func ConvertDateToGermanFormat(DateValue: Date) - String {
        let df = DateFormatter()
        //Nun legen wir das in Deutschland übliche Format fest
        df.dateFormat = "dd.MM.yyyy hh:mm:ss"
        //aus dem Date erzeugen wir wiederum ein String
        let sNewDateFormat = df.string(from: DateValue)
        return sNewDateFormat
}

Gibt nur das Datum im lokalen Format je nach Nutzer einstellung aus.
Das Ergebnis kann wie folgt aussehen: “12/31/2019”

let today = Date()
let formatter1 = DateFormatter()
formatter1.dateStyle = .short
print(formatter1.string(from: today))

Hier die Ausgabe von Datum und Uhrzeit abhängig von den regionalen Einstellungen:
Das Ergebnis kann so aussehen: “20:27:32” or “8:27:32pm”

let formatter2 = DateFormatter()
formatter2.timeStyle = .medium
print(formatter2.string(from: today))

Hier ein paar fertige Datumsfunktionen in einer Klasse:

class Tools {


    public static func ConvertDateToGermanFormat(DateValue: Date) -> String {
        let df = DateFormatter()
        //Nun legen wir das in Deutschland übliche Format fest
        df.dateFormat = "dd.MM.yyyy HH:mm:ss"
        //aus dem Date erzeugen wir wiederum ein String
        let sNewDateFormat = df.string(from: DateValue)
        return sNewDateFormat
    }

    public static func ConvertDateOnlyFormat(DateValue: Date) -> String {
           let df = DateFormatter()
           //Nun legen wir das in Deutschland übliche Format fest
           df.dateFormat = "dd.MM.yyyy"
           //aus dem Date erzeugen wir wiederum ein String
           let sNewDateFormat = df.string(from: DateValue)
           return sNewDateFormat
       }

    public static func ConvertDateToLocalFormat(DateValue: Date) -> String {
        let formatter = DateFormatter()
        formatter.timeStyle = .medium
        return formatter.string(from: DateValue)
    }

    public static func ConvertDateToLocalDateOnlyFormat(DateValue: Date) -> String {
         let formatter = DateFormatter()
         formatter.dateStyle = .medium
         return formatter.string(from: DateValue)
     }

    public static func ConvertStringToDate(DateValue: String) -> Date {
        let formatter = DateFormatter()
        let dValue = DateValue

        //formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
        var date = formatter.date(from: dValue)
        if (date==nil)
        {
            formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
                date = formatter.date(from: dValue)
        }
        return date!
    }



}
VonTobias Stephan

Swift String to Date function mit verschiedenen Formaten

Hier biete ich Dir eine kleine Klasse mit einer statischen Funktion, mit der Du problemlos ein String Wert in ein Date Format verwandeln kannst.

Datumsformate:
Wednesday, Sep 12, 2018           --> EEEE, MMM d, yyyy
09/12/2018                        --> MM/dd/yyyy
09-12-2018 14:11                  --> MM-dd-yyyy HH:mm
Sep 12, 2:11 PM                   --> MMM d, h:mm a
September 2018                    --> MMMM yyyy
Sep 12, 2018                      --> MMM d, yyyy
Wed, 12 Sep 2018 14:11:54 +0000   --> E, d MMM yyyy HH:mm:ss Z
2018-09-12T14:11:54+0000          --> yyyy-MM-dd'T'HH:mm:ssZ
12.09.18                          --> dd.MM.yy
10:41:02.112                      --> HH:mm:ss.SSS

Das passende Datumsformat habe ich hier fix in den Code gesetzt – das kannst Du natürlich auch noch hübscher machen.

class Tools {

    public static func ConvertStringToDate(DateValue: String) -> Date {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
        let date = formatter.date(from: DateValue)
        return date!
    }

}

Hier ein Praxisbeispiel. Der String Wert befindet sich in der auskommentierten Zeile. In diesem Beipsiel ging es darum, ein XML-File von einem Websearver auszulesen und ein Wiedervorlagedatum in das richtige Date Format zu bringen. Spätestens beim Debuggen erkennt man das vorliegende Format oder man schaut sich das XML-File einfach online an.

//s["Wiedervorlage"]    String?    "2020-02-09T15:24:21.693+01:00"    
let dDate :Date = Tools.ConvertStringToDate(DateValue: s["Wiedervorlage"]!)

Das Setzen des Gebietsschemas auf en_US_POSIX wird beim Parsen von Daten von einem Server dringend empfohlen, um Probleme mit den lokalen Einstellungen des Geräts zu vermeiden. Für einige Hintergrundinformationen darüber gibt bei den TECH NOTES von Apple.

formatter.locale = Locale(identifier: "en_US")
formatter.dateStyle = .short

formatter.string(from: date) // returns 12/10/2018

//Als Beispiel UK
formatter.locale = Locale(identifier: "en_GB")
formatter.string(from: date) // returns 10/12/18
//Man beachte das zweistellige Jahr

//...wenn wir in Deutschland sind...

formatter.locale = Locale(identifier: "de_DE")
formatter.string(from: date) // returns 10.12.18
Turns out the periods are the accepted standard. In addition, DateFormatter has already translated names for you, so you don't have to worry about it:

//...oder aber in Spanien
formatter.locale = Locale(identifier: "es_ES") 
formatter.dateStyle = .long
formatter.string(from: date) // returns "10 de diciembre, 2018"

VonTobias Stephan

SwiftUI load new view neue View anzeigen mit NavigationLink

Das ist ja wirklich mal einfach gelöst. Der NavigationLink umfasst hier die jeweils aus der Liste zu verlinkende Zeile.

struct ListoryListView: View {

        @ObservedObject var oListDatasource = ListDataSource()

        let oListoryParser = ListoryXMLParser()
        @State var selection = Set()
        var body: some View {
            NavigationView {

                List(){
                    ForEach(oListDatasource.ListOfListEntries)
                    { item in
                        NavigationLink(destination: EditEntryView()) {
                            VStack(alignment: .leading){
                                Text(item.Caption)


                            }

                            .onTapGesture {
                                print("\(item.Caption)")
                            }
                        }

                        //.navigationBarItems(trailing: EditButton())

                    }.onDelete(perform: xdelete)
                }
                    
                .navigationBarTitle(Text(self.oListDatasource.ActiveListTitle))
            .navigationBarItems(trailing:

                           Button("Help") {
                               print("Help tapped!")
                           }
                       )
            }

        }
VonTobias Stephan

Swift User defaults löschen delete remove

Manchmal möchte man die gespeicherten User Defaults auch einfach mal wieder entfernen. Mit diesem kleinen Code-Snippet machst Du wieder alles sauber.

  
let domain = Bundle.main.bundleIdentifier!
UserDefaults.standard.removePersistentDomain(forName: domain)
UserDefaults.standard.synchronize()
print(Array(UserDefaults.standard.dictionaryRepresentation().keys).count)