Category Archive Xcode Swift EN

ByTobias 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
ByTobias 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.

ByTobias 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")
]
ByTobias 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()
    }
}
ByTobias 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..<str.endIndex
        print("Range: " + str [rangeStartToEnd]) // "Hello, playground"

        let rangeWithoutGround = str.startIndex..<str.index(of: "ground")!
        print("Range: " + str[rangeWithoutGround]) // "Hello, playground"
    }

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..<str.index(before: str.endIndex)
str[range] // Hello, playgroun

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..<end
str[range] // play

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<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? {
        range(of: string, options: options)?.lowerBound
    }
    func endIndex<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> Index? {
        range(of: string, options: options)?.upperBound
    }
    func indices<S: StringProtocol>(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<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Range<Index>] {
        var result: [Range<Index>] = []
        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
    }
}
ByTobias 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.

<span class="s2">webKitView1</span><span class="s1">.</span>
scrollView
<span class="s1">.</span>
isScrollEnabled<span class="s1"> = </span>
<span class="s3"><b>false</b></span>
<span class="s1">
<span class="Apple-converted-space">&nbsp; &nbsp; &nbsp; </span></span>
<span class="s2">webKitView1</span><span class="s1">.</span>scrollView
<span class="s1">.</span>panGestureRecognizer<span class="s1">.</span>
isEnabled<span class="s1">=</span>
<span class="s3"><b>false
</b></span>
<span class="s2">webKitView1</span>
<span class="s1">.</span>
scrollView
<span class="s1">.</span>bounces<span class="s1"> = </span><span class="s3"><b>false</b></span>
<figure><img class="alignleft wp-image-541" src="http://www.exoda.de/wp-content/uploads/Bildschirmfoto-2019-10-05-um-13.29.41-300x55.png" alt="" width="676" height="124"></figure>