Autor-Archiv Tobias Stephan

VonTobias Stephan

swiftui imagepicker camera oder library

Du siehst hier ein komplettes Demo-Projekt, dass leicht wiederverwertbar ist. Einfach ein neues XCode Projekt anlegen und mit Copy-Paste in die Datei ContentView.Swift alles einfügen bzw. den vorhandenen standardmässig erstellten Code ersetzen. Ganz wichtig ist jedoch Deine erste Tat im neuen Projekt. Füge folgende Zeilen in die Info.plist ein.

NSCameraUsageDescription
Take Pictures
NSPhotoLibraryUsageDescription
Take Pictures

Wenn Du mit der rechten Maustaste auf Deine Info.plist klickst kannst du im Kontextmenü auf „Open as Source“ klicken. Diese Eigentschaften sind erforderlich, damit Deine App die erforderliche Berechtigung erhält auf die Photolibrary bzw. auf die Kamera zuzugreifen. Apple möchte dem User eine Begründung für den Zugriff nennen können. Sinnvolle Entscheidung. Vergisst man diesen Eintrag, kann das gleich zu einem Crash Deiner app führen.

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

import SwiftUI

struct ContentView: View {
    @State private var image: Image?
    
    @State private var showingImagePicker = false
    @State private var inputImage: UIImage?
    
    @State var ImagePickerSource: UIImagePickerController.SourceType = .photoLibrary
    
    var body: some View {
        NavigationView{
            VStack{
                ZStack{
                    Rectangle()
                        .fill(Color.secondary)
                    if image != nil {
                        image?
                            .resizable()
                            .scaledToFit()
                    } else {
                        Text("Tap to take photo!")
                            .foregroundColor(.white)
                            .font(.headline)
                    }
                }
                .onTapGesture {
                    self.showingImagePicker = true
                    self.ImagePickerSource = .camera
                }
                
                Button(action: {
                    self.showingImagePicker = true
                    self.ImagePickerSource = .photoLibrary
                })
                { Text("Library")}
                
                
            }
        }
        .padding([.horizontal, .bottom])
        .navigationBarTitle("Filter")
        .sheet(isPresented: $showingImagePicker, onDismiss: loadImage) {
            
            //   ImagePicker(image: self.$inputImage)
            ImagePicker(image: self.$inputImage, ImagePickerSource: self.ImagePickerSource)
            
        }
    }
    
    func loadImage() {
        guard let inputImage = inputImage else { return }
        image = Image(uiImage: inputImage)
    }
}

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


struct ImagePicker: UIViewControllerRepresentable {
    @Environment(\.presentationMode) var presentationMode
    @Binding var image: UIImage?
    @State var ImagePickerSource: UIImagePickerController.SourceType = .photoLibrary
    
    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        let parent: ImagePicker
        
        init(_ parent: ImagePicker) {
            self.parent = parent
            
        }
        
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
            
            if let uiImage = info[.originalImage] as? UIImage {
                parent.image = uiImage
            }
            
            parent.presentationMode.wrappedValue.dismiss()
        }
    }
    
    
    func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        picker.sourceType =  self.ImagePickerSource
        picker.allowsEditing = false
        return picker
    }
    
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext) {
        
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    
}

Und so sieht das Ergebnis dann aus:

Imagepicker Demo SwiftUI
ImagePicker Demo from Library or take aphoto
VonTobias Stephan

Swift UI Systemsound abspielen

So lässt sich mit einfachen Mitteln und wenig code ein Systemsound abspielen

// import this
import AVFoundation

// create a sound ID, in this case its the tweet sound.
let systemSoundID: SystemSoundID = 1016

// to play sound
AudioServicesPlaySystemSound (systemSoundID)

Als fertiges SwiftUI Sample:

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

import SwiftUI
import AVFoundation


struct ContentView: View {
    var body: some View {

        VStack {
            Button( action: {
                // import this


                // create a sound ID, in this case its the tweet sound.
                let systemSoundID: SystemSoundID = 1016

                // to play sound
                AudioServicesPlaySystemSound (systemSoundID)

            }){
                Text("Play Sound")
            }
        }
    }
}

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

Hier eine fertige Klasse für die Systemsounds:

//
//  SystemSounds.swift
//  Listory
//
//  Created by T. Stephan on 08.03.20.
//  Copyright © 2020 eCommerce - Tobias Stephan. All rights reserved.
//

import Foundation
import AVFoundation

class SystemSound {
    
    /**Spielt einen Systemsound ab*/
    public func PlaySystemSound(systemSoundID: SystemSoundID) {
        
        AudioServicesPlaySystemSound (systemSoundID)
    }
    


    public let Voicemail : SystemSoundID = 1015
    public let AudioToneBusy : SystemSoundID = 1070
    public let AudioToneError : SystemSoundID = 1073
    public let Telegraph : SystemSoundID = 1033

    var library = ["category" : [
        "-": ["Voicemail.caf": 1015],
        "AudioToneBusy" : ["ct-busy.caf": 1070],
        "AudioToneCallWaiting" : ["ct-call-waiting.caf" : 1074],
        "AudioToneCongestion" : ["ct-congestion.caf": 1071],
        "AudioToneError" : ["ct-error.caf" : 1073],
        "AudioToneKey2" : ["ct-keytone2.caf" : 1075],
        "AudioTonePathAcknowledge" : ["ct-path-ack.caf" : 1072],
        "BeginRecording" : ["begin_record.caf" : 1113],
        "BeginVideoRecording" : ["begin_video_record.caf" : 1117],
        "CalendarAlert" : ["alarm.caf" : 1005],
        "CameraShutter" : ["photoShutter.caf": 1108],
        "ConnectedToPower" : ["beep-beep.caf" : 1106],
        "EndRecording" : ["end_record.caf" : 1114],
        "EndVideoRecording" : ["end_video_record.caf" : 1118],
        "FailedUnlock" : ["-" : 1102],
        "Headset_AnswerCall" : ["short_low_high.caf" : 1256],
        "Headset_CallWaitingActions" : ["short_double_low.caf" : 1258],
        "Headset_EndCall" : ["short_double_low.caf": 1257],
        "Headset_Redial" : ["short_double_high.caf" : 1255],
        "Headset_StartCall" : ["long_low_short_high.caf" : 1254],
        "Headset_TransitionEnd" : ["middle_9_short_double_low.caf" : 1259],
        "JBL_Ambiguous" : ["jbl_ambiguous.caf" : 1115],
        "JBL_Begin" : ["jbl_begin.caf" : 1110],
        "JBL_Cancel" : ["jbl_cancel.caf" : 1112],
        "JBL_Confirm" : ["jbl_confirm.caf" : 1111],
        "JBL_NoMatch" : ["jbl_no_match.caf" : 1116],
        "KeyPressClickPreview" : ["Tock.caf" : 1306],
        "KeyPressed" : ["Tink.caf" : 1103,
                        "Tock.caf" : 1104,
                        "Tock1.caf" : 1105],
        "LowPower" : ["low_power.caf": 1006],
        "MailReceived" : ["new-mail.caf" : 1000],
        "MailSent" : ["mail-sent.caf" : 1001],
        "PINKeyPressed" : ["Tink.caf" : 1057],
        "RingerSwitchIndication" : ["RingerChanged.caf" : 1107],
        "RingerVibeChanged" : ["-" : 1350],
        "ScreenLocked" : ["lock.caf" : 1100],
        "ScreenUnlocked" : ["unlock.caf" : 1101],
        "ShakeToShuffle" : ["shake.caf" : 1109],
        "SilentVibeChanged" : ["-" : 1351],
        "SIMToolkitTone" : ["SIMToolkitCallDropped.caf" : 1051,
                            "SIMToolkitGeneralBeep.caf" : 1052,
                            "SIMToolkitNegativeACK.caf" : 1053,
                            "SIMToolkitPositiveACK.caf" : 1054,
                            "SIMToolkitSMS.caf" : 1055],
        "SMSReceived" : ["ReceivedMessage.caf" : 1003],
        "SMSReceived_Alert" : ["sms-received1.caf" : 1007,
                               "sms-received2.caf" : 1008,
                               "sms-received3.caf" : 1009,
                               "sms-received4.caf" : 1010,
                               "sms-received12.caf" : 1012,
                               "sms-received5.caf" : 1013,
                               "sms-received6.caf" : 1014,
                               "Anticipate.caf" : 1020,
                               "Bloom.caf" : 1021,
                               "Calypso.caf" : 1022,
                               "Choo_Choo.caf" : 1023,
                               "Descent.caf" : 1024,
                               "Fanfare.caf" : 1025,
                               "Ladder.caf" : 1026,
                               "Minuet.caf" : 1027,
                               "News_Flash.caf" : 1028,
                               "Noir.caf" : 1029,
                               "Sherwood_Forest.caf" : 1030,
                               "Spell.caf" : 1031,
                               "Suspense.caf" : 1032,
                               "Telegraph.caf" : 1033,
                               "Tiptoes.caf" : 1034,
                               "Typewriters.caf" : 1035,
                               "Update.caf" : 1036],
        "SMSReceived_Selection" : ["sms-received1.caf" : 1307,
                                   "sms-received2.caf" : 1308,
                                   "sms-received3.caf" : 1309,
                                   "sms-received4.caf" : 1310,
                                   "sms-received1_1.caf" : 1312,
                                   "sms-received5.caf" : 1313,
                                   "sms-received6.caf" : 1314,
                                   "Anticipate.caf" : 1320,
                                   "Bloom.caf" : 1321,
                                   "Calypso.caf" : 1322,
                                   "Choo_Choo.caf" : 1323,
                                   "Descent.caf" : 1324,
                                   "Fanfare.caf" : 1325,
                                   "Ladder.caf" : 1326,
                                   "Minuet.caf" : 1327,
                                   "News_Flash.caf" : 1328,
                                   "Noir.caf" : 1329,
                                   "Sherwood_Forest.caf" : 1330,
                                   "Spell.caf" : 1331,
                                   "Suspense.caf" : 1332,
                                   "Telegraph.caf" : 1333,
                                   "Tiptoes.caf" : 1334,
                                   "Typewriters.caf" : 1335,
                                   "Update.caf" : 1336,
                                   "-" : 1011,
                                   "--" : 1311],
        "SMSSent" : ["SentMessage.caf" : 1004,
                     "tweet_sent.caf" : 1016 ],
        "SystemSoundPreview" : ["Voicemail.caf" : 1300,
                                "ReceivedMessage.caf" : 1301,
                                "new-mail.caf" : 1302,
                                "mail-sent.caf" : 1303,
                                "alarm.caf" : 1304,
                                "lock.caf" : 1305,
                                "Voicemail_1.caf" : 1315],
        "TouchTone" : ["dtmf-0.caf" : 1200,
                       "dtmf-1.caf" : 1201,
                       "dtmf-2.caf" : 1202,
                       "dtmf-3.caf" : 1203,
                       "dtmf-4.caf" : 1204,
                       "dtmf-5.caf" : 1205,
                       "dtmf-6.caf" : 1206,
                       "dtmf-7.caf" : 1207,
                       "dtmf-8.caf" : 1208,
                       "dtmf-9.caf" : 1209,
                       "dtmf-star.caf" : 1210,
                       "dtmf-pound.caf" : 1211],
        "USSDAlert" : ["ussd.caf" : 1050],
        "VCCallUpgrade" : ["vc~ringing.caf" : 1154],
        "VCCallWaiting" : ["ct-call-waiting.caf" : 1153],
        "VCEnded" : ["vc~ended.caf" : 1152],
        "VCInvitationAccepted" : ["vc~invitation-accepted.caf" : 1150],
        "VCRinging" : ["vc~ringing.caf" : 1151],
        "Vibrate" : ["-" : 4095],
        "VoicemailReceived" : ["Voicemail.caf" : 1002]
        
        ]
    ]
}

VonTobias Stephan

Swift UI open Link URL in Safari Browser

Kurzes Snippet zum Öffnen eines Links in Safari in Swift.

if let url = URL(string: "https://www.exoda.de") {
    UIApplication.shared.open(url)
}
VonTobias Stephan

Swift UI Add Date Datum zu aktuellem Datum hinzurechnen

Mit einer einfachen Zeile Code einfach mal eine Anzahl Minuten zu dem aktuellen Datum hinzufügen.

let futureDate = Calendar.current.date(byAdding: .minute, value: 10080, to: Date())
VonTobias Stephan

Swift UI einfache Foreach Schleife 2D Array

So einfach lässt sich ein 2D Array durchlaufen.

for obj in self.oListDatasource.ListOfListEntries {
    print("rowID: \(String(obj.rowID))")
}

VonTobias Stephan

swiftui scenedelegate open view schemes deep link

Dieser Beitrag erklärt, wie man eine App mit entsprechender Aktion über einen Link öffnen kann. In diesem Beispiel könnte mann die App mit dem Link geileapp://wert öffnen.

Passe dafür zuerst Deine Info.plist an. Rechte Maustaste und dann als Sourcecode bearbeiten. Füge den Code für die Einträge einfach ein.

CFBundleURLTypes
	
		
			CFBundleURLName
			
			CFBundleURLSchemes
			
				geileapp
			
		
	

Nun brauchst Du noch diesen Code-Block in der SceneDelegate.swift. Das Ereignis wird ausgelöst, wenn jemand Die App über den Link hostet. Das was an Stelle von Wert im Link enthalten ist, findest Du dann in der Variablen context.url.host wieder und kannst entsprechend agieren. In diesem Beispiel wird die View NewView() geöffnet. Zum testen kannst Du einfach ein Standard-Projekt erstellen und auch gerne die ContentView() öffnen.

    func scene(_ scene: UIScene, openURLContexts URLContexts: Set) {
      for context in URLContexts {
        print("url: \(context.url.absoluteURL)")
        print("scheme: \(context.url.scheme)")
        print("host: \(context.url.host)")
        print("path: \(context.url.path)")
        print("components: \(context.url.pathComponents)")

      }
         let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
           let contentView = NewView().environment(\.managedObjectContext, context)

        if let windowScene = scene as? UIWindowScene {
              let window = UIWindow(windowScene: windowScene)
              window.rootViewController = UIHostingController(rootView: contentView)
              self.window = window
              window.makeKeyAndVisible()
          }
       // NewView().sStest = "x"
    }
VonTobias Stephan

SwiftUI Activity Indicator Demo Project

Eine ganze Reihe von Ansichten sind noch nicht in SwiftUI vertreten, aber es ist einfach, sie in das System zu portieren. Sie müssen UIActivityIndicator umbrechen und UIViewRepresentable machen.

import SwiftUI

struct ContentView: View {
    @State var isShowing = false
    var body: some View {
        LoadingView(isShowing: $isShowing) {
            NavigationView {
                List(["1", "2", "3", "4", "5"], id: \.self) { row in
                    Text(row)
                }.navigationBarTitle(Text("A List"), displayMode: .large)
                    .navigationBarItems(leading:
                        VStack{

                            Button(action: {
                                self.Show()

                            })
                            {
                                Text("Show")
                            }
                        }
                )
                    .onAppear() {
                        self.isShowing = true
                        //doing things
                        self.isShowing = false
                }
            }
        }
    }


    func Show() {
        isShowing = true;
    }
}

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


struct ActivityIndicator: UIViewRepresentable {

    @Binding var isAnimating: Bool
    let style: UIActivityIndicatorView.Style

    func makeUIView(context: UIViewRepresentableContext) -> UIActivityIndicatorView {
        return UIActivityIndicatorView(style: style)
    }

    func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext) {
        isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
    }
}

struct LoadingView: View where Content: View {

    @Binding var isShowing: Bool
    var content: () -> Content

    var body: some View {
        GeometryReader { geometry in
            ZStack(alignment: .center) {

                self.content()
                    .disabled(self.isShowing)
                    .blur(radius: self.isShowing ? 3 : 0)

                VStack {
                    Text("Loading...")
                    ActivityIndicator(isAnimating: .constant(true), style: .large)
                }
                .frame(width: geometry.size.width / 2,
                       height: geometry.size.height / 5)
                    .background(Color.secondary.colorInvert())
                    .foregroundColor(Color.primary)
                    .cornerRadius(20)
                    .opacity(self.isShowing ? 1 : 0)

            }
        }
    }

}

Download Swift UI Activity Indicator Demo Project
XCode Demo Projekt für den Activity Indicator unter SwiftUI
VonTobias Stephan

Flosaprint neue Version

In der neuen Flosaprint Version kann jetzt auch anstatt der FNSKU die SKU gedruckt werden. Für Anwender, die die FNSKU nutzen, empfehlen wir u.g. Einstellungen.

Wenn zum Druck der Artikeletiketten die FNSKU verwendet wird, gilt die Empfehlung:

235 x 150

VonTobias Stephan

Swift User defaults save array Speichern des Arrays

Hier ein Beispiel für das Speichern eines Arrays in den User defaults. In dem Array sind Wertpaare gespeichert. Die User defaults eignen sich für kleine Datenmengen und dazu zählt meiner Meinung auch ein kleines Array. Das ist eine schöne Methode um ein paar Daten abzulegen. In diesem Beispiel sind es 3 Datensätze, die abgelegt werden.

    func ArrayTest() ->  Int {
        let defaults = UserDefaults.standard
        
        let dict = ["Name": "Puppa","Strasse": "Meilershausener Str. 3","Ort": "00099 Testen"]
        
        defaults.set(dict, forKey: "myArray")
        
        
        let mySavedArray = defaults.object(forKey: "myArray") as? [String: String] ?? [String: String]()
        
        for s in mySavedArray {
            print(s)
        }
        
        return mySavedArray.count
        
    }

So sieht die Ausgabe aus:

(key: "Ort", value: "00099 Testen")
(key: "Strasse", value: "Meilershausener Str. 3")
(key: "Name", value: "Puppa")

Und natürlich noch ein Beispiel für ein eindimensionales Array:

  func ArrayTest1dimensional() ->  Int {
        let defaults = UserDefaults.standard

        let TheArray = ["a","b","c","d","e"]
     
        defaults.set(TheArray, forKey: "myArray2")


        let mySavedArray = defaults.object(forKey: "myArray2") as? [String] ?? [String]()

        for s in mySavedArray {
            print(s)
        }

        return mySavedArray.count

    }
VonTobias Stephan

SwiftUI textfield return key action

Ja, unter Swift UI ist alles ein bisschen anders – aber cool! Bei meinen ersten Versuchen haben absolute Kleinigkeiten eine halbe Ewigkeit gedauert. So auch hier. Ein einfach Wunsch! Einfach mal einen Event für Enter abgreifen. Damit Ihre nicht so lange suchen müsst, hier ein einfaches Beispiel wie man mit einem Return in einem TextField reagieren kann. Schliesslich drückt der iPhone bzw. IOS App Nutzer gerne auf Öffnen, anstatt einen Button zu klicken. Also geht kein Weg am Abgreifen eines Returns vorbei. Leg Dir einfach ein Testprojekt an und gib Dir die Zeit um um mit dem Beispiel zu spielen. Der Trick liegt hier im onCommit bzw. für den veränderten Wert des Textfelds onEditingChanged. Hier darfst Du dann eine andere Funktion aufrufen, um Dinge auszuführen.

import SwiftUI

struct ContentView: View {
    @State var output: String = ""
    @State var input: String = ""
    @State var typing = false
    var body: some View {
        VStack {
            if !typing {
                if !output.isEmpty {
                    Text("You typed: \(output)")
                }
            } else if !input.isEmpty {
                Text("You are typing: \(input)")
            }
            TextField("", text: $input, onEditingChanged: {
                self.typing = $0
            }, onCommit: {
                self.output = self.input
            })
            .background(Color.green.opacity(0.2))
        }
    }
}

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