Swift 2 Adding callouts to annotations - swift2

I'm having extreme difficulty having callouts added to my annotations. I've removed any attempt at adding the callout from the code below.
The annotations are added at the bottom of the updateVisiblePins function
import UIKit
import MapKit
import CoreLocation
import Foundation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
var locationManager = CLLocationManager()
var landmarkToPass: String!
var rowIndex: Int!
struct MyVars {
static var prevLoc = CLLocation()
static var region = MKCoordinateRegion()
static var landmarks = [Landmark]()
static var landmark: Landmark = Landmark(title: String(), locationName: String(), discipline: String(), coordinate: CLLocationCoordinate2D())
}
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager = CLLocationManager()
self.locationManager.delegate = self
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
self.mapView.showsUserLocation = true
var landmarks: [Landmark] = [Landmark]()
loadInitialData()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
var prevLoc = MyVars.prevLoc
let userLoction: CLLocation = locations[0]
let distance = calculateDisatnceBetweenTwoLocations(prevLoc, destination: userLoction)
if prevLoc != userLoction {
prevLoc = userLoction
MyVars.prevLoc = userLoction
if distance > 5 {
let latitude = userLoction.coordinate.latitude
let longitude = userLoction.coordinate.longitude
let latDelta: CLLocationDegrees = 0.05
let lonDelta: CLLocationDegrees = 0.05
let span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, lonDelta)
let location: CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
MyVars.region = MKCoordinateRegionMake(location, span)
self.mapView.showsUserLocation = true
self.mapView.setRegion(MyVars.region, animated: true)
updateVisiblePins()
} else {
let latitude = userLoction.coordinate.latitude
let longitude = userLoction.coordinate.longitude
let span = mapView.region.span
let location: CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
MyVars.region = MKCoordinateRegionMake(location, span)
self.mapView.showsUserLocation = true
updateVisiblePins()
}
}
}
func calculateDisatnceBetweenTwoLocations(source:CLLocation,destination:CLLocation) -> Double{
var distanceMeters = source.distanceFromLocation(destination)
var distanceKM = distanceMeters / 1000
return distanceKM
}
func updateVisiblePins() {
for (index, landmark) in MyVars.landmarks.enumerate() {
let landmarkLat = landmark.coordinate.latitude
let landmarkLon = landmark.coordinate.longitude
let userLocation = locationManager.location
let landmarkLocation = CLLocation(latitude: landmarkLat, longitude: landmarkLon)
let distance = calculateDisatnceBetweenTwoLocations(userLocation!, destination: landmarkLocation)
if distance < 30 {
mapView.addAnnotation(landmark)
} else {
if rowIndex != nil {
if index == rowIndex{
self.mapView.addAnnotation(landmark)
} else {
mapView.removeAnnotation(landmark)
}
}
}
}
}
func loadInitialData() {
// 1
let fileName = NSBundle.mainBundle().pathForResource("PublicLandmark", ofType: "json")
var data: NSData = NSData()
do {
data = try NSData(contentsOfFile: fileName!, options: [])
} catch {
}
// 2
var jsonObject: AnyObject!
do {
jsonObject = try NSJSONSerialization.JSONObjectWithData(data,
options: [])
} catch {
}
// 3
if let jsonObject = jsonObject as? [String: AnyObject],
// 4
let jsonData = JSONValue.fromObject(jsonObject)?["data"]?.array {
for landmarkJSON in jsonData {
if let landmarkJSON = landmarkJSON.array,landmark = Landmark.fromJSON(landmarkJSON) {
MyVars.landmarks.append(landmark)
}
}
}
}
}

The mapView.delegat = self line needs to be added to viewDidLoad() function.

Related

Is it possible to create 5 oscillators (an array?) working simultaneously and control their frequency and amplitude independently of each other?

import SwiftUI
import AudioKit
class ToneGenerator {
let engine = AudioEngine()
let osc = PlaygroundOscillator()
init(){
engine.output = osc
try! engine.start()
}
}
struct ContentView: View {
let toneGenerator = ToneGenerator()
var freq01: Float = 200
var volume01: Float = 0.5
#State var isPressed = false
var body: some View {
Text("BEEP")
.font((.title))
.simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged({ _ in
isPressed = true
toneGenerator.osc.frequency = freq01
toneGenerator.osc.amplitude = volume01
toneGenerator.osc.start()
})
.onEnded({ _ in
isPressed = false
toneGenerator.osc.stop()
})
)
}
}
I tried to create several generators, but did not understand how to do it.
All the materials I found on the Internet are related to the Playground and do not work in Xcode.
I'm not 100% sure what you're trying to achieve, but how about something like this. The key is to use $toneGenerators[index] to create a binding to the tone generator so you can change the volume and frequency.
class ToneGenerator: Identifiable {
let id = UUID()
let engine = AudioEngine()
var osc = PlaygroundOscillator()
init(){
engine.output = osc
try! engine.start()
}
}
struct ContentView: View {
#State var toneGenerators: [ToneGenerator] = []
let freqs = [Float(100), 200, 300, 400, 500]
var body: some View {
VStack {
HStack {
Button("BEEP") {
let toneGenerator = ToneGenerator()
toneGenerator.osc.frequency = Float.random(in: 100...1000)
toneGenerator.osc.amplitude = 0.5
toneGenerator.osc.start()
toneGenerators.append(toneGenerator)
}
.font((.title))
Button("STOP") {
toneGenerators.forEach { toneGenerator in
toneGenerator.osc.stop()
}
toneGenerators = []
}
.tint(.red)
.font((.title))
}
Text(toneGenerators.count, format: .number) + Text(" generators")
Grid {
GridRow {
Text("Freq").bold()
Text("Volume").bold()
}
ForEach(toneGenerators) { toneGenerator in
let index = toneGenerators.firstIndex(where: { $0.id == toneGenerator.id })!
GridRow {
Slider(value: $toneGenerators[index].osc.frequency, in: 100...1000)
Slider(value: $toneGenerators[index].osc.amplitude, in: 0...1)
}
}
}
.padding()
Spacer()
}
}
}

Capture video : SwiftUI

I want to capture a video through back camera using swiftUI. I can not find the proper solution on on video recording. I implement the code that record video automatically when view is open But I want to start the recording on bottom button click. Can someone please guide me on this.
import SwiftUI
import AVKit
struct RecordingView: View {
#State private var timer = 5
#State private var onComplete = false
#State private var recording = false
var body: some View {
ZStack {
VideoRecordingView(timeLeft: $timer, onComplete: $onComplete, recording: $recording)
VStack {
Button(action: {self.recording.toggle()}, label: {
ZStack {
Circle()
.fill(Color.white)
.frame(width: 65, height: 65)
Circle()
.stroke(Color.white,lineWidth: 2)
.frame(width: 75, height: 75)
}
})
Button(action: {
self.timer -= 1
print(self.timer)
}, label: {
Text("Toggle timer")
})
.foregroundColor(.white)
.padding()
Button(action: {
self.onComplete.toggle()
}, label: {
Text("Toggle completion")
})
.foregroundColor(.white)
.padding()
}
}
}
}
This is For recordingView
struct VideoRecordingView: UIViewRepresentable {
#Binding var timeLeft: Int
#Binding var onComplete: Bool
#Binding var recording: Bool
func makeUIView(context: UIViewRepresentableContext<VideoRecordingView>) -> PreviewView {
let recordingView = PreviewView()
recordingView.onComplete = {
self.onComplete = true
}
recordingView.onRecord = { timeLeft, totalShakes in
self.timeLeft = timeLeft
self.recording = true
}
recordingView.onReset = {
self.recording = false
self.timeLeft = 30
}
return recordingView
}
func updateUIView(_ uiViewController: PreviewView, context: UIViewRepresentableContext<VideoRecordingView>) {
}
}
extension PreviewView: AVCaptureFileOutputRecordingDelegate{
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
print(outputFileURL.absoluteString)
}
}
class PreviewView: UIView {
private var captureSession: AVCaptureSession?
private var shakeCountDown: Timer?
let videoFileOutput = AVCaptureMovieFileOutput()
var recordingDelegate:AVCaptureFileOutputRecordingDelegate!
var recorded = 0
var secondsToReachGoal = 30
var onRecord: ((Int, Int)->())?
var onReset: (() -> ())?
var onComplete: (() -> ())?
init() {
super.init(frame: .zero)
var allowedAccess = false
let blocker = DispatchGroup()
blocker.enter()
AVCaptureDevice.requestAccess(for: .video) { flag in
allowedAccess = flag
blocker.leave()
}
blocker.wait()
if !allowedAccess {
print("!!! NO ACCESS TO CAMERA")
return
}
// setup session
let session = AVCaptureSession()
session.beginConfiguration()
let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera,
for: .video, position: .front)
guard videoDevice != nil, let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice!), session.canAddInput(videoDeviceInput) else {
print("!!! NO CAMERA DETECTED")
return
}
session.addInput(videoDeviceInput)
session.commitConfiguration()
self.captureSession = session
}
override class var layerClass: AnyClass {
AVCaptureVideoPreviewLayer.self
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var videoPreviewLayer: AVCaptureVideoPreviewLayer {
return layer as! AVCaptureVideoPreviewLayer
}
override func didMoveToSuperview() {
super.didMoveToSuperview()
recordingDelegate = self
startTimers()
if nil != self.superview {
self.videoPreviewLayer.session = self.captureSession
self.videoPreviewLayer.videoGravity = .resizeAspect
self.captureSession?.startRunning()
self.startRecording()
} else {
self.captureSession?.stopRunning()
}
}
private func onTimerFires(){
print("🟢 RECORDING \(videoFileOutput.isRecording)")
secondsToReachGoal -= 1
recorded += 1
onRecord?(secondsToReachGoal, recorded)
if(secondsToReachGoal == 0){
stopRecording()
shakeCountDown?.invalidate()
shakeCountDown = nil
onComplete?()
videoFileOutput.stopRecording()
}
}
func startTimers(){
if shakeCountDown == nil {
shakeCountDown = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] (timer) in
self?.onTimerFires()
}
}
}
func startRecording(){
captureSession?.addOutput(videoFileOutput)
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let filePath = documentsURL.appendingPathComponent("tempPZDC")
videoFileOutput.startRecording(to: filePath, recordingDelegate: recordingDelegate)
}
func stopRecording(){
videoFileOutput.stopRecording()
print("🔴 RECORDING \(videoFileOutput.isRecording)")
}
}
Modify your code by this
struct VideoRecordingView: UIViewRepresentable {
#Binding var timeLeft: Int
#Binding var onComplete: Bool
#Binding var recording: Bool
func makeUIView(context: UIViewRepresentableContext<VideoRecordingView>) -> PreviewView {
let recordingView = PreviewView()
recordingView.onComplete = {
self.onComplete = true
}
recordingView.onRecord = { timeLeft, totalShakes in
self.timeLeft = timeLeft
self.recording = true
}
recordingView.onReset = {
self.recording = false
self.timeLeft = 30
}
return recordingView
}
func updateUIView(_ uiViewController: PreviewView, context: UIViewRepresentableContext<VideoRecordingView>) {
if recording {
uiViewController.start()
}
}
}
extension PreviewView: AVCaptureFileOutputRecordingDelegate{
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
print(outputFileURL.absoluteString)
}
}
class PreviewView: UIView {
private var captureSession: AVCaptureSession?
private var shakeCountDown: Timer?
let videoFileOutput = AVCaptureMovieFileOutput()
var recordingDelegate:AVCaptureFileOutputRecordingDelegate!
var recorded = 0
var secondsToReachGoal = 30
var onRecord: ((Int, Int)->())?
var onReset: (() -> ())?
var onComplete: (() -> ())?
init() {
super.init(frame: .zero)
var allowedAccess = false
let blocker = DispatchGroup()
blocker.enter()
AVCaptureDevice.requestAccess(for: .video) { flag in
allowedAccess = flag
blocker.leave()
}
blocker.wait()
if !allowedAccess {
print("!!! NO ACCESS TO CAMERA")
return
}
// setup session
let session = AVCaptureSession()
session.beginConfiguration()
let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera,
for: .video, position: .front)
guard videoDevice != nil, let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice!), session.canAddInput(videoDeviceInput) else {
print("!!! NO CAMERA DETECTED")
return
}
session.addInput(videoDeviceInput)
session.commitConfiguration()
self.captureSession = session
}
override class var layerClass: AnyClass {
AVCaptureVideoPreviewLayer.self
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var videoPreviewLayer: AVCaptureVideoPreviewLayer {
return layer as! AVCaptureVideoPreviewLayer
}
override func didMoveToSuperview() {
super.didMoveToSuperview()
recordingDelegate = self
}
func start() {
startTimers()
if nil != self.superview {
self.videoPreviewLayer.session = self.captureSession
self.videoPreviewLayer.videoGravity = .resizeAspect
self.captureSession?.startRunning()
self.startRecording()
} else {
self.captureSession?.stopRunning()
}
}
private func onTimerFires(){
print("🟢 RECORDING \(videoFileOutput.isRecording)")
secondsToReachGoal -= 1
recorded += 1
onRecord?(secondsToReachGoal, recorded)
if(secondsToReachGoal == 0){
stopRecording()
shakeCountDown?.invalidate()
shakeCountDown = nil
onComplete?()
videoFileOutput.stopRecording()
}
}
func startTimers(){
if shakeCountDown == nil {
shakeCountDown = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] (timer) in
self?.onTimerFires()
}
}
}
func startRecording(){
captureSession?.addOutput(videoFileOutput)
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let filePath = documentsURL.appendingPathComponent("tempPZDC")
videoFileOutput.startRecording(to: filePath, recordingDelegate: recordingDelegate)
}
func stopRecording(){
videoFileOutput.stopRecording()
print("🔴 RECORDING \(videoFileOutput.isRecording)")
}
}

How do I get a dropped pin inside a MapView to save coordinates and present them as a String using SwiftUI?

I made a Form using SwiftUI, in which I have a section that allows a user to press on a button that takes them to a MapKit View. Once inside the map view, the user can press the "+" button to place a pin on a map. This takes them to the Edit View where they can enter text inside a TextField to label the pin (see screenshot below). I have been stuck here for the past few days attempting to save the pin's coordinates or even the user's input inside the TextField to return it as text (either as city, state or country) inside the Form.
Form -> Map View -> Edit View
Here are some code snippets.
1) From FormView:
import SwiftUI
import MapKit
struct FormView: View {
#State private var selectedTitle = ""
#State var meuf: Meuf
#State private var meufs = [Meuf]()
#State private var show = false
#State private var singleIsPresented = false
#Environment(\.presentationMode) var presentationMode
let newMeuf : Bool
#EnvironmentObject var meufStorage : MeufStorage
#State private var showMap = false
var body: some View {
NavigationView{
Form{
//MARK: LOCATION
Section{
HStack {
Button(action: { self.showMap = true }) {
Image(systemName: "mappin.and.ellipse")
}
.sheet(isPresented: $showMap) {
LocationMap(showModal: self.$showMap)
}
Text("Pin your location")
.font(.subheadline)
}
}
// MARK: [ SAVE ENTRY ]
Section {
Button(action: {
if self.newMeuf {
self.saveData()
self.meufStorage.meufs.append(self.meuf)
} else {
for x in 0..<self.meufStorage.meufs.count {
if self.meufStorage.meufs[x].id == self.meuf.id {
self.meufStorage.meufs[x] = self.meuf
}
}
}
self.presentationMode.wrappedValue.dismiss()
}) {
HStack{
Spacer()
Text("Save")
Spacer()
}
}.disabled(meuf.title.isEmpty)
}
}.navigationBarTitle(Text(meuf.title))
}
}
//Get file directory url
func getFileDirectory() -> URL{
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
//Load data from file directory
func loadData(){
let filename = getFileDirectory().appendingPathComponent("SavedPlaces")
do {
let data = try Data(contentsOf: filename)
meufs = try JSONDecoder().decode([Meuf].self, from: data)
}catch{
debugPrint(error)
}
}
//Save data to file directory
func saveData(){
let filename = getFileDirectory().appendingPathComponent("SavedPlaces")
let data = try? JSONEncoder().encode(self.meufs)
do{
try data?.write(to: filename, options: [.atomic , .completeFileProtection])
}catch{
debugPrint(error)
}
}
}
struct FormView_Previews: PreviewProvider {
static var previews: some View {
FormView(meuf: Meuf(), newMeuf: true)
}
}
2) From LocationMap:
import SwiftUI
import MapKit
struct LocationMap: View {
#State private var centerCoordinate = CLLocationCoordinate2D()
#State private var locations = [CodableMKPointAnnotation]()
#State private var selectedPlace: MKPointAnnotation?
#State private var showingPlaceDetails = false
#State private var showingEditScreen = false
#Binding var showModal: Bool
var body: some View {
ZStack{
MapView(centerCoordinate: $centerCoordinate, annotations: locations, selectedPlace: $selectedPlace, showingPlaceDetails: $showingPlaceDetails)
.edgesIgnoringSafeArea(.all)
Circle()
.fill(Color.blue)
.opacity(0.3)
.frame(width: 32, height: 32)
VStack {
Spacer()
HStack{
Spacer()
Button(action:{
let newLocation = CodableMKPointAnnotation()
newLocation.title = ""
newLocation.coordinate = self.centerCoordinate
self.locations.append(newLocation)
self.selectedPlace = newLocation
self.showingEditScreen = true
}){
Image(systemName: "plus")
}
.padding()
.background(Color.black.opacity(0.7))
.foregroundColor(Color.white)
.clipShape(Circle())
.shadow(radius: 0.7)
.padding([.trailing , .bottom])
}
}
.padding()
}.alert(isPresented: $showingPlaceDetails) {
Alert(title: Text(selectedPlace?.title ?? "Unknown"), message: Text(selectedPlace?.subtitle ?? "Missing place information."), primaryButton: .default(Text("OK")), secondaryButton: .default(Text("Edit")) {
self.showingEditScreen = true
}
)
}
.sheet(isPresented: $showingEditScreen, onDismiss: savedData) {
if self.selectedPlace != nil {
EditView(placemark: self.selectedPlace!)
}
}
.onAppear(perform: loadData)
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
func loadData() {
let filename = getDocumentsDirectory().appendingPathComponent("Saved Places")
do {
let data = try Data(contentsOf: filename)
locations = try JSONDecoder().decode([CodableMKPointAnnotation].self, from: data)
} catch {
print("Unable to load saved data.")
}
}
func savedData() {
do {
let filename = getDocumentsDirectory().appendingPathComponent("SavedPlaces")
let data = try JSONEncoder().encode(self.locations)
try data.write(to: filename, options: [.atomicWrite, .completeFileProtection])
} catch {
print("Unable to save data")
}
}
}
struct LocationMap_Previews: PreviewProvider {
static var previews: some View {
LocationMap(showModal: .constant(true))
}
}
3) From MapView:
import MapKit
import Combine
import SwiftUI
struct MapView: UIViewRepresentable {
#Binding var centerCoordinate: CLLocationCoordinate2D
var annotations: [MKPointAnnotation]
#Binding var selectedPlace: MKPointAnnotation?
#Binding var showingPlaceDetails: Bool
func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
let mapView = MKMapView()
mapView.delegate = context.coordinator
return mapView
}
func updateUIView(_ uiView: MKMapView, context: UIViewRepresentableContext<MapView>) {
if annotations.count != uiView.annotations.count{
uiView.removeAnnotations(uiView.annotations)
uiView.addAnnotations(annotations)
}
}
///Coordinator class for passing data
class Coordinator: NSObject , MKMapViewDelegate{
let parent: MapView
init(_ parent: MapView){
self.parent = parent
}
func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
parent.centerCoordinate = mapView.centerCoordinate
}
//Gets called whenever the rightCalloutAccessory is tapped
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
guard let placeMark = view.annotation as? MKPointAnnotation else {return}
parent.selectedPlace = placeMark
parent.showingPlaceDetails = true
}
//Customizes the way the marker looks
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "PlaceMark"
var annotationview = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationview == nil {
annotationview = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationview?.canShowCallout = true
annotationview?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}else {
annotationview?.annotation = annotation
}
return annotationview
}
}
func makeCoordinator() -> MapView.Coordinator {
Coordinator(self)
}
}
extension MKPointAnnotation {
static var example: MKPointAnnotation {
let annotation = MKPointAnnotation()
annotation.title = "Montreal"
annotation.subtitle = "Home of French Canadians"
annotation.coordinate = CLLocationCoordinate2D(latitude: 45.5, longitude: -73.58)
return annotation
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView(centerCoordinate: .constant(MKPointAnnotation.example.coordinate), annotations: [MKPointAnnotation.example], selectedPlace: .constant(MKPointAnnotation.example), showingPlaceDetails: .constant(false))
}
}
4) From Edit View:
import SwiftUI
import MapKit
struct EditView: View {
#Environment(\.presentationMode) var presentationMode
#ObservedObject var placemark: MKPointAnnotation
var body: some View {
NavigationView {
Form {
Section {
TextField("Place name", text: $placemark.wrappedTitle)
TextField("Description", text: $placemark.wrappedSubtitle)
}
}
.navigationBarTitle("Edit place")
.navigationBarItems(trailing: Button("Done") {
self.presentationMode.wrappedValue.dismiss()
})
}
}
}
struct EditView_Previews: PreviewProvider {
static var previews: some View {
EditView(placemark: MKPointAnnotation.example)
}
}
5) MKPointAnnotation Codable
import Foundation
import MapKit
class CodableMKPointAnnotation: MKPointAnnotation , Codable {
enum codingKeys: CodingKey {
case title ,subtitle , longitude , latitude
}
override init() {
super.init()
}
public required init(from decoder: Decoder) throws{
super.init()
let container = try decoder.container(keyedBy: codingKeys.self)
title = try container.decode(String.self, forKey: .title)
subtitle = try container.decode(String.self, forKey: .subtitle)
let latitude = try container.decode(CLLocationDegrees.self, forKey: .latitude)
let longitude = try container.decode(CLLocationDegrees.self, forKey: .longitude)
coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: codingKeys.self)
try container.encode(title, forKey: .title)
try container.encode(subtitle, forKey: .subtitle)
try container.encode(coordinate.latitude, forKey: .latitude)
try container.encode(coordinate.longitude, forKey: .longitude)
}
}
6) MKPointAnnotation Object
import MapKit
extension MKPointAnnotation: ObservableObject{
public var wrappedTitle: String{
get{
self.title ?? "No Title"
}
set{
self.title = newValue
}
}
public var wrappedSubtitle: String{
get{
self.subtitle ?? "No information on this location"
}
set{
self.subtitle = newValue
}
}
}
7) Meuf & MeufStorage:
import Foundation
struct Meuf: Identifiable, Encodable, Decodable {
var id = UUID()
var img = ""
var title = ""
var rating = 3.0
var seen = false
var seenDate = ""
}
class MeufStorage: ObservableObject {
#Published var meufs = [Meuf]()
}
8) Scene Delegate:
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let meufStorage = MeufStorage()
let contentView = MeufList().environment(\.managedObjectContext, context).environmentObject(meufStorage)
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidEnterBackground(_ scene: UIScene) {
(UIApplication.shared.delegate as? AppDelegate)?.saveContext()
}
}
Hello I managed to get the solution.
Will add code only which is changed
// Made this a ObservableObject
class Meuf: Identifiable, Codable, ObservableObject {
var id = UUID()
var img = ""
var title = ""
var rating = 3.0
var seen = false
var seenDate = ""
var locations = [CodableMKPointAnnotation]() // We need this to keep the track of locations
}
FormView
struct FormView: View {
#State private var selectedTitle = ""
#ObservedObject var meufObject = Meuf() // This is new will help to keep track of the added locations
#State private var meufs = [Meuf]()
#State private var show = false
#State private var singleIsPresented = false
#Environment(\.presentationMode) var presentationMode
#EnvironmentObject var meufStorage : MeufStorage
#State private var showMap = false
var body: some View {
NavigationView{
Form {
List {
// This will list the added locations now
ForEach(self.meufObject.locations, id: \.self) { location in
LocationView(location: location)
}
}
//MARK: LOCATION
Section{
HStack {
Button(action: { self.showMap = true }) {
Image(systemName: "mappin.and.ellipse")
}
.sheet(isPresented: $showMap) {
LocationMap(meufObject: self.meufObject, showModal: self.$showMap)
}
Text("Pin your location")
.font(.subheadline)
}
}
// MARK: [ SAVE ENTRY ]
Section {
Button(action: {
// Handle save action
}) {
HStack{
Spacer()
Text("Save")
Spacer()
}
}
}
}
}
}
// Rest of your code stays same .......
}
// Added this new view to render the location view
struct LocationView: View {
var location : CodableMKPointAnnotation
var body: some View {
Text(location.title ?? "title" )
}
}
LocationMap
struct LocationMap: View {
#ObservedObject var meufObject: Meuf // This is new will help to keep track of the added locations
#State private var centerCoordinate = CLLocationCoordinate2D()
#State private var locations = [CodableMKPointAnnotation]()
#State private var selectedPlace: MKPointAnnotation?
#State private var showingPlaceDetails = false
#State private var showingEditScreen = false
#Environment(\.presentationMode) var presentationMode
#Binding var showModal: Bool
var body: some View {
ZStack{
MapView(centerCoordinate: $centerCoordinate, annotations: locations, selectedPlace: $selectedPlace, showingPlaceDetails: $showingPlaceDetails)
.edgesIgnoringSafeArea(.all)
Circle()
.fill(Color.blue)
.opacity(0.3)
.frame(width: 32, height: 32)
VStack {
Spacer()
HStack{
Spacer()
Button(action:{
let newLocation = CodableMKPointAnnotation()
newLocation.title = ""
newLocation.coordinate = self.centerCoordinate
self.locations.append(newLocation)
self.meufObject.locations = self.locations // By doing this we will be able to pass it to main screen
self.selectedPlace = newLocation
self.showingEditScreen = true
}){
Image(systemName: "plus")
}
.padding()
.background(Color.black.opacity(0.7))
.foregroundColor(Color.white)
.clipShape(Circle())
.shadow(radius: 0.7)
.padding([.trailing , .bottom])
// Rest stays same as your implementation
}
}
.padding()
}
}
// Rest stays same as your implementation
}

PayU Money Gateway iOS Swift

I want to integrate the payU Money sdk in my app using swift2.0
I am using this sdk: https://github.com/payu-intrepos/Documentations/wiki/8.1-NEW-iOS-Seamless-SDK-integration
Where to create the test account for testing.
import UIKit
var merchantKey="your live merchant key"
var salt="your live merchant salt"
var PayUBaseUrl="https://secure.payu.in"
class PaymentScreen: UIViewController,UIWebViewDelegate {
#IBOutlet weak var myWebView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
self.initPayment()
}
func initPayment() {
var i = arc4random()
let amount = "1"
let productInfo = "Order"
let firstName = "Sample name"
let email = "abc#gmail.com"
let phone = "9999119911"
let sUrl = "https://www.google.com"
let fUrl = "https://www.bing.com"
let service_provider = "payu_paisa"
let strHash:String = self.sha1(String.localizedStringWithFormat("%d%#", i, NSDate()))
let rangeOfHello = Range(start: strHash.startIndex,
end: strHash.startIndex.advancedBy(20))
let txnid1 = strHash.substringWithRange(rangeOfHello)
let hashValue = String.localizedStringWithFormat("%#|%#|%#|%#|%#|%#|||||||||||%#",merchantKey,txnid1,amount,productInfo,firstName,email,salt)
let hash=self.sha1(hashValue)
let postStr = "txnid="+txnid1+"&key="+merchantKey+"&amount="+amount+"&productinfo="+productInfo+"&firstname="+firstName+"&email="+email+"&phone="+phone+"&surl="+sUrl+"&furl="+fUrl+"&hash="+hash+"&service_provider="+service_provider
let url = NSURL(string: String.localizedStringWithFormat("%#/_payment", PayUBaseUrl))
print("check my url", url, postStr)
let request = NSMutableURLRequest(URL: url!)
do {
let postLength = String.localizedStringWithFormat("%lu",postStr.characters.count)
request.HTTPMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Current-Type")
request.setValue(postLength, forHTTPHeaderField: "Content-Length")
request.HTTPBody = postStr.dataUsingEncoding(NSUTF8StringEncoding)
myWebView.loadRequest(request)
} catch {
}
}
func webViewDidStartLoad(webView: UIWebView) {
}
func webViewDidFinishLoad(webView: UIWebView) {
let requestURL = self.myWebView.request?.URL
let requestString:String = (requestURL?.absoluteString)!
if requestString.containsString("https://www.google.com") {
print("success payment done")
}
else if requestString.containsString("https://www.bing.com") {
print("payment failure")
}
}
func webView(webView: UIWebView, didFailLoadWithError error: NSError?) {
print("double failure")
}
func sha1(toEncrypt:String) -> String {
let data = toEncrypt.dataUsingEncoding(NSUTF8StringEncoding)!
var digest = [UInt8](count:Int(CC_SHA512_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA512(data.bytes, CC_LONG(data.length), &digest)
let hexBytes = digest.map { String(format: "%02x", $0) }
return hexBytes.joinWithSeparator("")
}
}
// Swift 4
import UIKit
import SystemConfiguration
import Foundation
class PayUMoneyViewController: UIViewController, UIWebViewDelegate, UIAlertViewDelegate {
#IBOutlet weak var webView: UIWebView!
var merchantKey = "YOUR_MARCHANT_KEY"
var salt = "YOUR_SALT_KEY"
var PayUBaseUrl = "https://secure.payu.in"
var SUCCESS_URL = "https://www.payumoney.com/payments/guestcheckout/#/success"
var FAILED_URL = "https://www.PayUmoney.com/mobileapp/PayUmoney/failure.php"
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
var request = NSMutableURLRequest()
override func viewDidLoad() {
super.viewDidLoad()
self.webView.delegate = self
self.payPayment()
self.navigationItem.hidesBackButton = true
let newBackButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.plain, target: self, action: #selector(PayUMoneyViewController.back(sender:)))
self.navigationItem.leftBarButtonItem = newBackButton
}
#objc func back(sender: UIBarButtonItem) {
let alert = UIAlertController(title: "Cancel !", message: "Do you really want to cancel the transaction ?", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.default, handler: cancelTransaction))
alert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
func cancelTransaction( action : UIAlertAction)
{
_ = navigationController?.popToRootViewController(animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func payPayment() {
var i = arc4random()
let amount = "1"
let productInfo = "Transport"
let firstName = USER_FIRST_NAME // Geet
let email = USER_EMAIL // geetbasakare#gmail.com
let phone = USER_MOBILE_NO // 1234567890
let sUrl = "https://www.google.com"
let fUrl = "https://www.bing.com"
let service_provider = "payu_paisa"
let strHash:String = self.sha1(toEncrypt: String.localizedStringWithFormat("%d%#", i, NSDate()));
let r1 = strHash.range(of: strHash)!
// String range to NSRange:
let n1 = NSRange(r1, in: strHash)
print((strHash as NSString).substring(with: n1)) //
// NSRange back to String range:
let r2 = Range(n1, in: strHash)!
print(strHash.substring(with: r2))
let rangeOfHello = Range(n1, in: strHash)!
let txnid1 = strHash.substring(with: rangeOfHello)
let hashValue = String.localizedStringWithFormat("%#|%#|%#|%#|%#|%#|||||||||||%#",merchantKey,txnid1,amount,productInfo,firstName,email,salt)
let hash = self.sha1(toEncrypt: hashValue)
let postStr = "txnid="+txnid1+"&key="+merchantKey+"&amount="+amount+"&productinfo="+productInfo+"&firstname="+firstName+"&email="+email+"&phone="+phone+"&surl="+sUrl+"&furl="+fUrl+"&hash="+hash+"&service_provider="+service_provider
let url = NSURL(string: String.localizedStringWithFormat("%#/_payment", PayUBaseUrl))
print("check my url", url as Any, postStr)
let request = NSMutableURLRequest(url: url! as URL)
do {
let postLength = String.localizedStringWithFormat("%lu",postStr.characters.count)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Current-Type")
request.setValue(postLength, forHTTPHeaderField: "Content-Length")
request.httpBody = postStr.data(using: String.Encoding.utf8)
webView.loadRequest(request as URLRequest)
} catch {
print(error)
}
}
func webViewDidFinishLoad(_ webView: UIWebView) {
let requestURL = self.webView.request?.url
let requestString:String = (requestURL?.absoluteString)!
if (requestString == SUCCESS_URL) {
print("success payment done")
}
else if (requestString == FAILED_URL) {
print("payment failure")
}
}
func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
print("double failure")
}
func sha1(toEncrypt: String) -> String {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA512_DIGEST_LENGTH))
if let data = toEncrypt.data(using: String.Encoding.utf8) {
let value = data as NSData
CC_SHA512(value.bytes, CC_LONG(data.count), &digest)
}
var digestHex = ""
for index in 0..<Int(CC_SHA512_DIGEST_LENGTH) {
digestHex += String(format: "%02x", digest[index])
}
return digestHex
}
/*
func sha1(toEncrypt:String) -> String {
var data = toEncrypt.data(using: String.Encoding.utf8)!
var digest = [UInt8](repeating: 0, count:Int(CC_SHA512_DIGEST_LENGTH))
_ = data.withUnsafeBytes {messageBytes in
CC_SHA512(messageBytes, CC_LONG(data.count), &digest)
}
let hexBytes = digest.map { String(format: "%02x", $0) }
return hexBytes.joined(separator: "")
}
*/
}
You can create a test account as described in the following help documentation, and you need not perform KYC in this regard. Later, you can use the Key and Salt on the Payment Gateway page of the Dashboard.
https://devguide.payu.in/low-code-web-sdk/getting-started-low-code-web-sdk/register-for-a-test-merchant-account/

Trouble Debugging NSTimer

I am trying to have an image show up for 1 second and disappear after the 1 second is up. When I build my app it works fine but when I run my app it crashes. Here is my code:
var flashTimer = NSTimer()
var timerCounter = 0
var currentTime = 0
var randomImageGeneratorNumber = 0
var flashingImageView = UIImageView()
var flashButton = UIButton()
#IBAction func flashButton(sender: UIButton) {
var randomImageGeneratorNumber = arc4random_uniform(6) + 1
if flashButton.hidden {
flashButton.hidden = true
flashingImageView.hidden = false
}
if randomImageGeneratorNumber == 1 {
flashingImageView.image = UIImage(named: "Image1.png")
}
if randomImageGeneratorNumber == 2 {
flashingImageView.image = UIImage(named: "Image2.png")
}
if randomImageGeneratorNumber == 3 {
flashingImageView.image = UIImage(named: "Image3.png")
}
if randomImageGeneratorNumber == 4 {
flashingImageView.image = UIImage(named: "Image4.png")
}
if randomImageGeneratorNumber == 5 {
flashingImageView.image = UIImage(named: "Image5.png")
}
if randomImageGeneratorNumber == 6 {
flashingImageView.image = UIImage(named: "Image6.png")
}
var currentTime = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "timerFunc", userInfo: nil, repeats: false)
func timerFunc(sender: AnyObject){
flashingImageView.hidden = true;
}
}
The error in the debugger output is:
[Flash_Facts.ViewController timerFunc]: unrecognized selector sent to instance 0x7f9ceb774810
I am not sure how to fix this. Any suggestions?
Thanks in advance.
The problem is that you have to create your function outside the IBAction function. The same applies to your timer var ( currentTime )
var currentTime = NSTimer()
func timerFunc() {
flashButton.hidden = !flashButton.hidden
flashingImageView.hidden = !flashingImageView.hidden
}
#IBAction func flashButton(sender: UIButton) {
flashButton.hidden = !flashButton.hidden
flashingImageView.hidden = !flashingImageView.hidden
flashingImageView.image = UIImage(named: "Image\(arc4random_uniform(6) + 1).png")
currentTime = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "timerFunc", userInfo: nil, repeats: false)
}

Resources