Kategorien-Archiv Xcode Swift DE

VonTobias Stephan

Xcode Swift UI Canvas Leinwand wird nicht angezeigt

Aus mir nicht erfindlichen Gründen wurde bei einem SwiftUI File einfach die Leinwand (von Canvas) nicht mehr angezeigt. Googeln half auch nicht. Catalina 10.15 setze ich ein. Schließlich hat es ja auch vorher funktioniert. Im gleichen Projekt mit anderen SwiftUI Files wurde die Preview auch angezeigt. Also empfehle ich einen flotten Workaround. Copy + Paste ist hier die Lösung. Einfach ein neues File anlegen und das alte Löschen. Quellcode wieder einfügen und schwups…die Preview ist wieder da. Natürlich sollte man vorher prüfen, ob die Leinwand überhaupt eingeblendet wird.

Über das Menü Editor + Canvas kann die Leinwand ein- bzw. ausgeblendet werden. Eine weitere Möglichkeit ist die
Tastenkombination: ALT – Command – RETURN

Hier im Bild kann man auf das Symbol rechts oben, 2. von rechts bemühen.

VonTobias Stephan

Swift print variable in string

So gibt man einfach den Wert einer Variable in Swift innerhalb eines strings aus.

let str = "Einfach mal den Wert einer Variable ausgeben..."
print("\(str!)")
VonTobias Stephan

SwiftUI TableView ListView example sample code project

SwiftUI ist meiner Meinung nach ein absoluter Meilenstein in der Softwareentwicklung. Sicherlich kostet es Zeit sich in diese neue Art des Entwickelns einzufinden, aber der Zeitaufwand, den man bei weiteren Projekten einspart, kann man hier gut investieren. Hier findest Du ein einfaches Beispiel für ein SwiftUI TableView. Das Sample Project kannst Du einfach runterladen und frei verwenden. In dem Projekt ist bewusst nicht mehr enthalten, damit die wesentlichen Funktionen zum Verständnis beitragen.

Download XCode Sample Project.

Es ist schon beeindruckend, mit wie wenig Zeilen Code man ein TableView / ListView mit SwiftUI zaubern kann. Einfach ein neues Projekt anlegen und darauf achten, daß als User Interface SwiftUI ausgewählt wird.

SwiftUI Interace selektieren

Natürlich benötigst Du noch 3 Bilder für diese Beispiel, die sind ebenfalls in dem Beispielprojekt enthalten.

Assets für die Bilder in der Liste

Der eigentliche Code für das Projekt.

import SwiftUI

struct ContentView: View {
    var oListArray: [oListenEntries] = testData
    var body: some View {
        List(oListArray) { item in
            Image(item.imageName).resizable().frame(width: 32.0, height: 20.0)
            VStack(alignment: .leading){
                Text(item.make)
                Text(item.model)
                    .font(.subheadline)
                    .foregroundColor(Color.gray)
            }
        }
    }

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

Hier noch der Code für das zugrundeliegende Array. Dafür habe ich ein SwiftUI View mit dem Namen oListEntries.swift angelegt.

import SwiftUI

struct oListenEntries : Identifiable {
    var id = UUID()
    var make: String;
    var model: String;
    var imageName: String { return make }
}

let testData = [
    oListenEntries(make: "Flaschenhalter", model: "für Balkon oder Pool"),
    oListenEntries(make: "Pooladapter", model: "32 mm auf 12 mm"),
    oListenEntries(make: "Sektglashalter", model: "schwimmend")
]
VonTobias Stephan

Swiftui tabbar selecteditem auswählen

Bei den ersten Gehversuchen mit Swift UI habe ich krampfhaft versucht herauszufinden, wie man das aktive Tab programmatisch auswählt. Der Trick liegt im Binding.

@State public var selection = 0

Mit Klick auf den Button erzählt selection den Wert eins. Da Selection direkt an die Tabbar gebunden ist, ändert sich das aktive Tabitem wunschgemäß.

Button("Go",action: {self.selection = 1})

Binding ist das Zugriffstoken, das Sie weitergeben können, um direkten Lese- und Schreibzugriff auf den Wert zu ermöglichen, ohne den Besitz (im Sinne der Beibehaltung eines Referenztyps) oder das Kopieren (für einen Werttyp) zu gewähren.

Wenn der Benutzer eine Registerkarte in der Registerkartenansicht auswählt, ändert er den Wert einseitig über Bindung und weist der ausgewähltenTab-Variablen das zugehörige .tag(…) zu. Dies funktioniert für @State und ObservableObject auf die gleiche Weise.

Der Programmierer kann dieser ausgewähltenTab-Variablen auch jederzeit einen Wert zuweisen – und die TabView schaltet die angezeigte Registerkarte sofort um.
Dies ist der Schlüssel zur programmatischen Navigation in SwiftUI.

Hier ist ein Beispiel, daß man direkt Copy / Paste übernehmen kann um damit rumzuspielen.

import SwiftUI

struct ContentView: View {
    @State public var selection = 0

    let defaults = UserDefaults.standard

    var body: some View {
        TabView(selection: $selection){
            VStack {
                Text("First View")
                Button("Go",action: {self.selection = 1})
            }
            .font(.title)
            .tabItem {
                VStack {
                    Image("first")
                    Text("First")
                }
            }
            .tag(0)
            Text("Second View")

                .font(.title)
                .tabItem {
                    VStack {
                        HStack{
                            Image("second")
                            Text("Second")
                        }
                    }
            }
            .tag(1)
        }
    }
}



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

Searchbar mit 2D Array verbinden und ein TableView durchsuchen Swift 5.1

Hier findest Du die wesentlichen Dinge, die Du brauchst um ein TableView zu durchsuchen. Zuerst ziehst Du mal eine Searchbar aus dem Objektkatalog und erstellst im Code ein Outlet.

Nachdem Du das Outlet im Quelltext gesetzt hast, setze das delegate.

Jetzt benötigen wir noch eine Variable „searching“ sowie ein weiteres 2-dimensionales Array. Die Variable searching bekommt den Wert true, während eine Suche stattfindet. Hier im Beispiel siehst du die Variable für das 2. Array, dass baugleich mit dem Array results ist. In das 2. Array wird jeweils das Filterergebnis gesetzt. Jetzt weißt Du, warum wir die Boolean Variable „searching“ benötigen. So kann man für alle weiteren Aktionen im Code abfragen, aus welchem Array man sich gerne bedienen möchte. Je nachdem ob halt gerade gesucht wird oder die Komplette Liste im Tableview dargestellt wird.

var searching = false
var filteredresults: [[String: String]]? = [[String: String]]()
Nun diese extension hinzufügen. Hier wird das 2. Array filteredresults mit dem Filterergebnis gefüllt und festgelegt ob unsere Variable searching true oder false gesetzt wird. Logischerweise wird
seraching = false
gesetzt, wenn
filteredresults!.count == 0
Datensätze enthält.
extension ViewController: UISearchBarDelegate {
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
       
        filteredresults = self.results!.filter ({
            
            $0["ASIN"]!.localizedCaseInsensitiveContains(searchText)

        })
        if(filteredresults!.count == 0){
            searching = false
        }else{
            searching = true
        }

        self.tableView.reloadData()

    }
}
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        if (searching){
            return filteredresults!.count
        }else {
            return (results!.count)
        }
    }

Beim füllen der Felder in der Tableview Cell darauf achten, daß auch hier aus dem richtigen Array abgegriffen wird.

if (searching){

            cell.lblASIN.text = filteredresults![indexPath.row]["ASIN"]
        }
        else
        {
            cell.lblASIN.text = results![indexPath.row]["ASIN"]
        }

Hier solltest Du natürlich auch darauf achten, dass numberOfRowsInSection jeh nach Suchzustand korrekt zurückgeliefert wird.

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        if (searching){
            return filteredresults!.count
        }else {
            return (results!.count)
        }
    }

VonTobias Stephan

Wann wird meine App im Store von Apple angezeigt?

Ich berichte hier einfach mal aus Erfahrung. Die Theorie sagt, dass App’s im App Store von Apple nach 24 Stunden in der Suche aufgefunden werden. In der Praxis durchaus nach wenigen Minuten. Wir sprechen hier von App’s, die gerade das Approval erhalten haben und zum ersten mal im App-Store zur Verfügung stehen. Wenn Deine App gerade frisch das Approval erhalten hat, ist Sie nicht automatisch sofort auffindbar.

Apple Appstore Approval

Trotzdem darfst Du Dich freuen, wenn Du diese Mail bekommst. Das Approval kann doch manchmal ein nervender Prozess sein. Am Ende entscheiden Menschen und keine Algorithmen. Menschen sind in Ihren Entscheidungen nun einmal subjektiv. Sicherlich kann jeder App-Entwickler von seinen Erfahrungen berichten. Hier gilt aber, einfach nicht aufgeben. Eine App kann durchaus auch mal abgelehnt werden, obwohl Sie schon lange zugelassen wurde. Der nächste Mitarbeiter von Apple schaut vielleicht auch mal etwas genauer hin und hat etwas zu meckern. In den meisten Fällen haben Sie bei genauer Betrachtung recht – nur manchmal wird es etwas merkwürdig. Dann gilt stumpf…App an die Anforderungen anpassen.

VonTobias Stephan

Swift Datum formatieren

Hier jetzt mal in ganz einfach. In diesem Beispiel habe ich mit Swift 5.1 gearbeitet.

Wir möchten das Datum im Format:
2020-01-06T19:23:43.97+01:00 – ISO8601
in das Format:
06.01.2020 07:23:43
konvertieren.

   let dateFormatter = DateFormatter()
   //Wir erhalten das Ausgangsformat ISO8601
   //Beispiel: 2020-01-06T19:23:43.97+01:00
   dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
   //Das Datum wird aus einem XML Stream geladen
   let stringDate : String = oAMZXMLParser.results![0]["Logdatum"]!
   print(stringDate)
   //Aus dem String wird in das Date Format konvertiert
   let date = dateFormatter.date(from: stringDate)

   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: date!)
   print(sNewDateFormat)

Jetzt liegt es an Dir andere Formate zu wählen.

VonTobias Stephan

Swift Substring to String

Herrjeh…diese String-Operationen in Swift können einen schon wahnsinnig machen, wenn man aus C# ein einfaches gewohnt ist. Deshalb habe ich mich damit auseinander gesetzt und hier ein wenig zusammengetragen. Getestet wurde das mit Swift 5.1

  • startIndex ist der Index des ersten Zeichens
  • endIndex ist der Index nach dem letzten Zeichen
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        let str = "Hello, playground"
        print(str[str.startIndex]) // H
        //print(str[str.endIndex])   // error: after last character

        let rangeStartToEnd = str.startIndex..



before bezieht sich auf den Index des Zeichens direkt vor dem angegebenen Index.

// character
let index = str.index(before: str.endIndex)
str[index] 

// range
let range = str.startIndex..



Der OffsetBy-Wert kann positiv oder negativ sein und beginnt ab dem angegebenen Index. Obwohl er vom Typ String.IndexDistance ist, können Sie einen Int Wert übergeben.

// character
let index = str.index(str.startIndex, offsetBy: 7)
str[index]

// range
let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let range = start..



Das limitedBy ist nützlich, um sicherzustellen, dass der Offset nicht dazu führt, dass der Index über die Grenzen hinausgeht. Es ist ein Begrenzungsindex. Da es möglich ist, dass der Offset die Grenze überschreitet, gibt diese Methode ein Optional zurück. Sie gibt null zurück, wenn der Index außerhalb der Grenzen liegt.

if let index = str.index(str.startIndex, offsetBy: 7, limitedBy: str.endIndex) {
    str[index]
}

Wäre der Offset 77 statt 7 gewesen, dann wäre die if-Anweisung übersprungen worden.

Warum denn nun der ganze Umstand?

Es wäre viel einfacher, einen Int-Index für Strings zu verwenden. Der Grund dafür, dass Sie für jeden String einen neuen String.Index erstellen müssen, ist, dass die Zeichen in Swift nicht alle gleich lang unter der Haube sind. Ein einzelnes Swift-Zeichen kann aus einem, zwei oder sogar mehreren Unicode-Codepunkten bestehen. Daher muss jeder eindeutige String die Indizes seiner Zeichen berechnen.

Es ist möglich, diese Komplexität hinter einer Int-Indexerweiterung zu verbergen, aber ich zögere, dies zu tun. Es ist gut, an das erinnert zu werden, was tatsächlich passiert.

Übersetzt mit www.DeepL.com/Translator (kostenlose Version)

Eine hilfreiche Extension

Folgende Extenson muss noch unter Deine Klasse im Code gehängt werden. Diese Extension bietet Dir die Möglichkeit innerhalb eines String gleich den Index einer ganzen Zeichenfolge zu ermitteln. In meinem Beispiel "ground".

String-Operationen werden also über Indices und Ranges angesprochen. Der Index ist also keine einfache Integervariable.

extension StringProtocol {
    func index(of string: S, options: String.CompareOptions = []) -> Index? {
        range(of: string, options: options)?.lowerBound
    }
    func endIndex(of string: S, options: String.CompareOptions = []) -> Index? {
        range(of: string, options: options)?.upperBound
    }
    func indices(of string: S, options: String.CompareOptions = []) -> [Index] {
        var indices: [Index] = []
        var startIndex = self.startIndex
        while startIndex < endIndex,
            let range = self[startIndex...]
                .range(of: string, options: options) {
                indices.append(range.lowerBound)
                startIndex = range.lowerBound < range.upperBound ? range.upperBound :
                    index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
        }
        return indices
    }
    func ranges(of string: S, options: String.CompareOptions = []) -> [Range] {
        var result: [Range] = []
        var startIndex = self.startIndex
        while startIndex < endIndex,
            let range = self[startIndex...]
                .range(of: string, options: options) {
                result.append(range)
                startIndex = range.lowerBound < range.upperBound ? range.upperBound :
                    index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
        }
        return result
    }
}
VonTobias Stephan

Swift String Zeichen abschneiden

So konvertiert man einen Substring in einen String
Swift: 5.1

Swift hat einen dedizierten Substring-Typ (String.SubSequence), der für die Aufnahme von Teilen eins Strings ausgelegt ist, was eine Leistungsoptimierung darstellt: Wenn Sie einen Teil eines Strings in einer anderen Variablen speichern, kann Swift den Substring einfach auf den übergeordneten String zeigen, anstatt alle Daten zu kopieren.

Obwohl Teilzeichenketten auf viele der gleichen Arten wie normale Zeichenketten verwendet werden können, sind sie nicht gleich – wenn Sie eine Funktion haben, die eine Zeichenkette als Parameter akzeptiert, können Sie ihr einfach keine Teilzeichenkette senden.

Um dies zu beheben, können Sie Ihren Substring in einen String-Initialisierer wie folgt einpacken:

let quote = "The revolution will be Swift"
let substring = quote.dropFirst(23)
let realString = String(substring)
VonTobias Stephan

Swift 5 Timer Event Beispiel Sample

Dieses Timer Event Beispiel kannst Du einfach in die viewDidLoad() Methode packen.

Im Ausgabe Fenster kannst du verfolgenden, wie das Ereignet alle 5 Sekunden „gefeuert“ wird.

let timer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { timer in
print("Timer fired!")

}
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd hh:mm:ss"
let sDate = df.string(from: timer.fireDate)
print(sDate)