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.
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() } }
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") } } }
Styles:
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() } }
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.
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.
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.
Of course you still need 3 images for this example, they are also included in the example project.
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") ]
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() } }
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
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 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 } }
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"> </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>