Kategorien-Archiv Xcode Swift EN

VonTobias Stephan

Generate Regex from String

Link to the Regex-Generator

I myself was looking for such a generator, because that is quite a fiddle. With this generator you can easily convert a string into a regex. This is a great basis to adjust the generated regex pattern. For this I use one of the numerous regex testers. There are enough of those.

VonTobias Stephan

SwiftUI Menue Button example Hamburger Menu

Here you go. Here you have a ready sample for a Swift UI hamburger menu. You can easily hide the menu with a slide or tap on the hamburger symbol. Simply create a new SwiftUI project as a single app with the default settings. Simply copy and paste everything and play around with the project.

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

import SwiftUI

struct ContentView: View {
    @State var showHamburgerMenu = false
    
    
    var body: some View {
        
        
        let drag = DragGesture()
            .onEnded {
                if $0.translation.width < -100 {
                    withAnimation {
                        self.showHamburgerMenu = false
                    }
                }
        }
        
        return NavigationView {
            GeometryReader { geometry in
                ZStack(alignment: .leading) {
                    MainView(showHamburgerMenu: self.$showHamburgerMenu)
                        .frame(width: geometry.size.width, height: geometry.size.height)
                        .offset(x: self.showHamburgerMenu ? geometry.size.width/2 : 0)
                        .disabled(self.showHamburgerMenu ? true : false)
                    
                    if self.showHamburgerMenu {
                        MenuView()
                            .frame(width: geometry.size.width/2)
                            .transition(.move(edge: .leading))
                    }
                }
                .gesture(drag)
                
            }
            .navigationBarTitle("Side Menu", displayMode: .inline)
            .navigationBarItems(leading: (
                Button(action: {
                    withAnimation {
                        self.showHamburgerMenu.toggle()
                    }
                }) {
                    Image(systemName: "line.horizontal.3")
                        .imageScale(.large)
                }
            ))
        }
        
    }
}

struct MainView: View {
    @Binding var showHamburgerMenu: Bool
    
    var body: some View {
        Button(action: {
            withAnimation {
                self.showHamburgerMenu = true
            }
        }) {
            Text("Show Menu")
        }
    }
}

struct MenuView: View {
    var body: some View {
        VStack(alignment: .leading) {
            HStack {
                Image(systemName: "person")
                    .foregroundColor(.gray)
                    .imageScale(.large)
                NavigationLink(destination: ProfileView()) {
                    Text("Profile")
                        .foregroundColor(.gray)
                        .font(.headline)
                }
            }
            .padding(.top, 100)
            HStack {
                Image(systemName: "envelope")
                    .foregroundColor(.gray)
                    .imageScale(.large)
                NavigationLink(destination: MessagesView()) {
                    Text("Messages")
                        .foregroundColor(.gray)
                        .font(.headline)
                }
            }
            .padding(.top, 30)
            HStack {
                Image(systemName: "gear")
                    .foregroundColor(.gray)
                    .imageScale(.large)
                NavigationLink(destination: SettingsView()) {
                    Text("Settings")
                        .foregroundColor(.gray)
                        .font(.headline)
                }
            }
            .padding(.top, 30)
            Spacer()
        }
        .padding()
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(Color(red: 32/255, green: 32/255, blue: 32/255))
        .edgesIgnoringSafeArea(.all)
    }
}

struct SettingsView: View {
    var body: some View {
        VStack{
            Text("Settings Subview")
        }
    }
}
struct MessagesView: View {
    var body: some View {
        VStack{
            Text("Messages Subview")
        }
    }
}
struct ProfileView: View {
    var body: some View {
        VStack{
            Text("Profile Subview")
        }
    }
}

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

VonTobias Stephan

swiftui multiple pickers in a form

This is the first time I have used the Picker under SwiftUI. Simple insight – the OS decides which appearance your picker takes on. Of course you can decide that yourself. Now I had the strange behaviour that when I selected an entry of 5 pickers, all pickers were run through. My pickers are in a form. In this form a NavigationView. Solution of the problem was the Section. This example is not suitable for copy-paste because you are missing the data source. This is for illustration purposes only.

import SwiftUI

struct ContentView: View {
    @State public var selectedCrossSection = 0
    @State private var selectedColor = 0
    @State private var selectedLength = 0
    @State private var selectedEyelet1 = 0
    @State private var selectedEyelet2 = 0

    var selCrossSectionStr = ""

    @ObservedObject var oListDatasource = ListDataSource()
    var body: some View {

        NavigationView{


            VStack (spacing: 10) {
                Form {
                    Section {
                        Picker(selection: self.$selectedCrossSection, label: Text("Querschnitt")) {

                            ForEach(self.oListDatasource.CrossSections, id: \.self) { item in
                                Text(item.CrossSection).tag(item.rowIndex)
                            }
                        }
                            //.pickerStyle(SegmentedPickerStyle()).foregroundColor(Color.orange)

                            .font(Font.custom("ArialMT", size: 12))
                            .onReceive([self.selectedCrossSection].publisher.first()) { (value) in
                                print(self.oListDatasource.CrossSections[self.selectedCrossSection].CrossSection)

                        }
                    }
                    Section {
                        Picker(selection: self.$selectedColor, label: Text("Farbe")) {

                            ForEach(self.oListDatasource.Colors) { item in
                                Text(item.Color).tag(item.rowIndex)
                            }
                        }
                            //.pickerStyle(SegmentedPickerStyle()).foregroundColor(Color.orange)
                            .font(Font.custom("ArialMT", size: 12))
                    }

                    Section {
                        Picker(selection: self.$selectedLength, label: Text("Länge")) {

                            ForEach(self.oListDatasource.Lengths) { item in
                                Text(item.Length).tag(item.rowIndex)
                            }
                        }
                        .font(Font.custom("ArialMT", size: 12))
                    }

                Section {
                    Picker(selection: $selectedEyelet1, label: Text("Öse 1")) {

                        ForEach(oListDatasource.Eyelets) { item in
                            Text(item.Eyelet).tag(item.rowIndex)
                        }
                    }
                    .font(Font.custom("ArialMT", size: 12))
                }
                Section {
                    Picker(selection: $selectedEyelet2, label: Text("Öse 2")) {

                        ForEach(oListDatasource.Eyelets) { item in
                            Text(item.Eyelet).tag(item.rowIndex)
                        }
                    }
                    .font(Font.custom("ArialMT", size: 12))
                }
                }

            NavigationLink(destination: ListItems(selectedCrossSection: selectedCrossSection)) {
                Text("Liste").frame(width: 100)

            }
            }


            .navigationBarTitle("Kabelshop")

        }


    }
}
Mehrere Picker in einer View

Styles:

  • Navigation view style.
  • Wheel picker.
  • Segmented picker.
  • Date picker style.
VonTobias Stephan

SwiftUI toggle action events

The toggle switch in SwiftUI unfortunately has no „action“ event like the button. If you enter a function call in the curly brackets, you will get the following error message:

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

The trick is to create a view here.

In the example code you will find the line:

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

That’s solution. Take the ToggleAction function and expand it for your purposes. In this example, the status variable is simply entered as return value.

//
//  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

swift canvas window not showing

For reasons I can’t explain, the canvas (from Canvas) was simply no longer displayed in a SwiftUI file. Googling did not help either. I use Catalina 10.15. After all it worked before. In the same project with other SwiftUI files the preview was also displayed. So I recommend a quick workaround. Copy + Paste is the solution here. Just create a new file and delete the old one. Insert the source code again and poof…the preview is back. Of course you should check if the canvas is shown at all.

With the menu Editor + Canvas the canvas can be shown or hidden. Another possibility is the
Key combination: ALT – Command – RETURN

Here in the picture you can click on the symbol on the top right, 2. from the right.

VonTobias Stephan

SwiftUI TableView ListView example sample code project

In my opinion SwiftUI is an absolute milestone in software development. Certainly it takes time to get used to this new way of developing, but the time you save on further projects can be well invested. Here you can find a simple example of a SwiftUI TableView. The Sample Project can be downloaded and used freely. The project deliberately does not contain more, so that the essential functions contribute to the understanding.

Download XCode Sample Project.

It is quite impressive how few lines of code you can create a TableView / ListView with SwiftUI. Just create a new project and make sure that SwiftUI is selected as user interface.

SwiftUI Interace selektieren

Of course you still need 3 images for this example, they are also included in the example project.

Assets für die Bilder in der Liste

The actual code for the project.

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)
        }
    }
}

Here is the code for the underlying array. For this I created a SwiftUI view with the name oListEntries.swift.

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 change

During my first attempts with Swift UI, I tried desperately to figure out how to select the active tab programmatically. The trick is in the binding.

@State public var selection = 0

With a click on the button selection tells the value one. Since Selection is directly linked to the tab bar, the active tab item changes as desired.

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

Binding is the access token that you can pass to allow direct read and write access to the value without granting possession (in the sense of retaining a reference type) or copying (for a value type).

When the user selects a tab in the tab view, he or she changes the value unilaterally via binding and assigns the corresponding .tag(…) to the selectedTab variable. This works the same way for @State and ObservableObject.

The programmer can also assign a value to this selectedTab variable at any time – and the TabView immediately switches the displayed tab.

This is the key to programmatic navigation in SwiftUI.

Here is an example that you can copy / paste directly to play around with it.

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

Swift Substring to String Conversion

These string operations in Swift can drive you crazy if you are used to a simple one from C#. So, I’ve been looking into it and I’ve put together a little bit here. I tested it with Swift 5.1

  • startIndex is the index of the first character
  • endIndex is the index after the last character
 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 refers to the index of the character directly before the specified index.

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

// range
let range = str.startIndex..



The OffsetBy value may be positive or negative and starts from the specified index. Although it is of type String.IndexDistance, you can pass an Int value.

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

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



The limitedBy is useful to ensure that the offset does not cause the index to exceed the limits. It is a limit index. Since it is possible for the offset to exceed the limit, this method returns an optional. It returns zero if the index is outside the limits.

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

If the offset had been 77 instead of 7, the if statement would have been skipped.

It would be much easier to use an Int index for strings. The reason you need to create a new String.index for each string is because the characters in Swift are not all the same length under the hood. A single Swift character can consist of one, two or even more Unicode code points. Therefore, each unique string must calculate the indices of its characters.

It is possible to hide this complexity behind an Int index extension, but I hesitate to do so. It's good to be reminded of what actually happens.

Translated with www.DeepL.com/Translator (free version)

The following Extenson must be added under your class in the code. This extension offers you the possibility to determine the index of a whole string within a string. In my example "ground".

String operations are thus addressed via indices and ranges. The index is therefore not a simple integer variable.

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

webkit disable scrolling in swift

If you do not want the webview to be scrollable, you can easily disable it with the following lines of code.

webKitView1.
scrollView
.
isScrollEnabled = 
false

      
webKitView1.scrollView
.panGestureRecognizer.
isEnabled=
false

webKitView1
.
scrollView
.bounces = false