Swift 5 Delegates Does not Listen the Event - delegates

protocol NewHomeVCDelegate {
func didEditHabit(_ habit: Habit)
}
class NewHomeVC: {
var newHomeVCDelegate : NewHomeVCDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
func didButtonTapped() {
self.newHomeVCDelegate?.didEditHabit(habit)
let vc = NewCreateHabitVC()
vc.modalPresentationStyle = .popover
self.present(vc, animated: true)
}
}
class NewCreateHabitVC: NewHomeVCDelegate {
func didEditHabit(_ habit: Habit) {
updateUI(habit: habit)
}
override func viewDidLoad() {
super.viewDidLoad()
setViews()
setDelegates()
addGestureRecognizer()
var newHomeVC = NewHomeVC()
newHomeVC.newHomeVCDelegate = self
}
func updateUI(habit: Habit) {
// it does some stuff here
}
}
I am trying to pass habit object from NewHomeVC to NewCreateHabitVC via delegate and protocol. However the problem is that the delegate does not listen. Can someone show me what I am doing wrong here? Thanks.

struct Habit {
let name : String
enum CodingKeys: String, CodingKey {
case name = "name"
}
}
protocol NewHomeVCDelegate {
func didEditHabit(_ habit: Habit)}
class NewHomeVC: UIViewController , NewHomeVCDelegate{
//var newHomeVCDelegate : NewHomeVCDelegate?
let myHabit = Habit(name: "I want to edit this habit")
override func viewDidLoad() {
super.viewDidLoad()
}
func didButtonTapped() {
//no need here
//self.newHomeVCDelegate?.didEditHabit(habit)
//when you present the ViewController you the set the delegate, so he can ping you back once done. Also you need to set the object that you intend to edit
let vc = NewCreateHabitVC()
vc.newHomeVCDelegate = self
vc.habitThatIwantToEdit = myHabit
vc.modalPresentationStyle = .popover
self.present(vc, animated: true)
}
//you have to implement this method in home class
func didEditHabit(_ habit: Habit) {
//this object is the final edited object from NewCreateHabitVC, this will be called on finish button press, here you have it.
}
}
class NewCreateHabitVC: UIViewController, NewHomeVCDelegate {
//but here
var newHomeVCDelegate : NewHomeVCDelegate?
var habitThatIwantToEdit :Habit?
func didEditHabit(_ habit: Habit) {
//
//updateUI(habit: habit)
}
override func viewDidLoad() {
super.viewDidLoad()
//setViews()
//setDelegates()
//addGestureRecognizer()
//var newHomeVC = NewHomeVC()
//newHomeVC.newHomeVCDelegate = self
if let _ = habitThatIwantToEdit{
updateUI(habit: habitThatIwantToEdit!)
}else{
print("object not set")
}
}
func updateUI(habit: Habit) {
// it does some stuff here
}
func finishButtonTaped(){
if let habit = habitThatIwantToEdit{
newHomeVCDelegate?.didEditHabit(habit)
}
}
}

Related

Search for places/ locations using MapKit and Search Bar (SwiftUI, Xcode 12.4)

I have a question about how one can connect a Search Bar with MapKit, so that it is able to search for places/ locations (not using StoryBoard). I have already written the code for the Search Bar and for the MapView in separate files, but even after trying literally every code and tutorial on the internet, I couldn't find a way to connect the Search Bar to search for locations. Below one can see respectively the used SearchBar.swift file, the MapViewController.swift and a snippet of the ContentView.swift.
SearchBar.swift
import UIKit
import Foundation
import SwiftUI
import MapKit
struct SearchBar: UIViewRepresentable {
// Binding: A property wrapper type that can read and write a value owned by a source of truth.
#Binding var text: String
// NSObject: The root class of most Objective-C class hierarchies, from which subclasses inherit a basic interface to the runtime system and the ability to behave as Objective-C objects.
// UISearchBarDelegate: A collection of optional methods that you implement to make a search bar control functional.
class Coordinator: NSObject, UISearchBarDelegate {
#Binding var text: String
let Map = MapViewController()
init(text: Binding<String>) {
_text = text
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
text = searchText
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
text = ""
searchBar.showsCancelButton = true
searchBar.endEditing(true)
searchBar.resignFirstResponder()
}
}
func makeCoordinator() -> SearchBar.Coordinator {
return Coordinator(text: $text)
}
func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar {
let searchBar = UISearchBar(frame: .zero)
searchBar.delegate = context.coordinator
searchBar.showsCancelButton = true
searchBar.searchBarStyle = .minimal
//searchBar.backgroundColor = .opaqueSeparator
searchBar.showsCancelButton = true
return searchBar
}
func updateUIView(_ uiView: UIViewType, context: Context) {
uiView.text = text
}
}
MapViewController.swift
class MapViewController: UIViewController, CLLocationManagerDelegate {
let mapView = MKMapView()
let locationManager = CLLocationManager()
#Published var permissionDenied = false
override func viewDidLoad() {
super.viewDidLoad()
setupMapView()
checkLocationServices()
}
func setupMapView() {
view.addSubview(mapView)
mapView.translatesAutoresizingMaskIntoConstraints = false
mapView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
mapView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor).isActive = true
mapView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor).isActive = true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let span = MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)
guard let location = locations.last else { return }
let region = MKCoordinateRegion(center: location.coordinate, span: span)
mapView.setRegion(region, animated: true)
let categories:[MKPointOfInterestCategory] = [.cafe, .restaurant]
let filters = MKPointOfInterestFilter(including: categories)
mapView.pointOfInterestFilter = .some(filters)
// Enables the scrolling around the user location without hopping back
locationManager.stopUpdatingLocation()
}
func checkLocalAuthorization() {
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
mapView.showsUserLocation = true
followUserLocation()
locationManager.startUpdatingLocation()
break
case .denied:
permissionDenied.toggle()
break
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted:
// Show alert
break
case .authorizedAlways:
break
#unknown default:
fatalError()
}
}
func checkLocationServices() {
if CLLocationManager.locationServicesEnabled() {
setupLocationManager()
checkLocalAuthorization()
} else {
// user did not turn it on
}
}
func followUserLocation() {
if let location = locationManager.location?.coordinate {
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: 4000, longitudinalMeters: 4000)
mapView.setRegion(region, animated: true)
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
checkLocalAuthorization()
}
func setupLocationManager() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
}
The methods are then called in the ContentView.swift, using these methods:
struct MapViewRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> some UIViewController {
return MapViewController()
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}
struct ContentView: View {
#State private var searchText : String = ""
var body: some View {
ZStack(alignment: .top) {
MapViewRepresentable()
.edgesIgnoringSafeArea(.all)
.onTapGesture {
self.endTextEditing()
}
SearchBar(text: $searchText)
}
}
}
Is it possible to connect both like I explained, or is there another method you advice? I really hope you guys can help me! Thanks in advance :)

XCode Page View Controller: make dots transparent

i have added a page view controller and a 3 pages.
I set Transition Style to Scroll and implemented the presentationCount and the presentationIndex method. now i get a black bar with gray/white dots at the bottom of my view. However, but i want the view to goright to the bottom and the make the dots appear over it(without the black background.
How do i do that?
here is my code:
import UIKit
class FilterViewController: UIPageViewController, UIPageViewControllerDataSource {
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
if let firstViewController = orderedViewControllers.first {
setViewControllers([firstViewController],
direction: .forward,
animated: true,
completion: nil)
}
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
private(set) lazy var orderedViewControllers: [UIViewController] = {
return [self.newViewController(category: "first"),
self.newViewController(category: "second"),
self.newViewController(category: "third")]
}()
private func newViewController(category: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil) .
instantiateViewController(withIdentifier: "\(category)ViewController")
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
let previousIndex = viewControllerIndex - 1
guard previousIndex >= 0 else {
return orderedViewControllers.last
}
guard orderedViewControllers.count > previousIndex else {
return nil
}
return orderedViewControllers[previousIndex]
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
return nil
}
let nextIndex = viewControllerIndex + 1
let orderedViewControllersCount = orderedViewControllers.count
guard orderedViewControllersCount != nextIndex else {
return orderedViewControllers.first
}
guard orderedViewControllersCount > nextIndex else {
return nil
}
return orderedViewControllers[nextIndex]
}
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return orderedViewControllers.count
}
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
guard let firstViewController = viewControllers?.first,
let firstViewControllerIndex = orderedViewControllers.index(of: firstViewController) else {
return 0
}
return firstViewControllerIndex
}
}
ok, i got it myself. If anyone has the same problem, just override the viewDidLayoutSubviews() of the Page View Controller:
override func viewDidLayoutSubviews() {
let v = self.view
let subviews = v?.subviews
if subviews?.count == 2 {
var sv:UIScrollView?
var pc:UIPageControl?
for t in subviews! {
if t is UIScrollView {
sv = t as! UIScrollView
} else {
pc = t as! UIPageControl
}
}
if(sv != nil && pc != nil) {
sv?.frame = (v?.bounds)!
v?.bringSubview(toFront: pc!)
}
}
super.viewDidLayoutSubviews()
}

Swift 3D Touch Quick Action not loading requested url

I'm new to Swift, and trying my hands with UIWebView app that loads default url, with option to perform quick action and load a different url.
Problem is when I request the quick action url, code executes but the new url is not loading. So I'm missing something in the flow somewhere.
Here is the code:
import UIKit
import WebKit
class ViewController: UIViewController, UIWebViewDelegate {
#IBOutlet var webView: UIWebView!
override func loadView() {
super.loadView()
self.webView = UIWebView()
self.view = self.webView!
}
override func viewDidLoad() {
print("view did load")
super.viewDidLoad()
let url = NSURL(string: "google.com")
let req = NSURLRequest(URL:url!)
webView.loadRequest(req)
webView.delegate = self
}
func loadUrl2() {
loadView()
let url = NSURL(string: "example.com")
print(url)
let req = NSURLRequest(URL:url!)
self.webView!.loadRequest(req)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I was experimenting and added loadView to loadUrl2, as I was getting
fatal error: unexpectedly found nil while unwrapping an Optional value
before that.
Edited to Include loading secondary link:
Here are the changes and files you'll need to make to the App Delegate
enum ShortcutIdentifier: String {
case OpenNewLink
case OpenBetterLink
init?(fullIdentifier: String) {
guard let shortIdentifier = fullIdentifier.componentsSeparatedByString(".").last else {
return nil
}
self.init(rawValue: shortIdentifier)
}
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsAnnotationKey] as? UIApplicationShortcutItem {
handleShortcut(shortcutItem)
return false
}
return true
}
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
completionHandler(handleShortcut(shortcutItem))
}
private func handleShortcut(shortcutItem: UIApplicationShortcutItem) -> Bool {
let shortcutType = shortcutItem.type
guard let ShortcutIdentifier = ShortcutIdentifier(fullIdentifier: shortcutType) else {
return false
}
return selectLinkForIdentifier(ShortcutIdentifier)
}
private func selectLinkForIdentifier(identifier: ShortcutIdentifier) -> Bool {
guard let mainView = self.window?.rootViewController as? ViewController else {
return false
}
switch identifier {
case .OpenNewLink:
mainView.urlString = "http://www.bing.com"
mainView.loadWebView(mainView.urlString)
return true
case.OpenBetterLink:
mainView.urlString = "http://www.duckduckgo.com"
mainView.loadWebView(mainView.urlString)
return true
}
}
I also made changes in the MainVC
class ViewController: UIViewController, UIWebViewDelegate {
#IBOutlet var webView: UIWebView!
var urlString: String? = nil
override func viewDidLoad() {
super.viewDidLoad()
setUpWebView()
webView.delegate = self
view.addSubview(webView)
}
func setUpWebView() {
webView = UIWebView()
webView.frame = CGRectMake(0, 0, view.frame.width, view.frame.height)
loadWebView(urlString)
}
func loadWebView(var urlString: String?) {
if urlString == nil {
urlString = "http://www.google.com"
}
let url = NSURL(string: urlString!)
let req = NSURLRequest(URL:url!)
webView.loadRequest(req)
}
}
Be sure to add NSAppTransportSecurity dictionary to your .plist and add NSAllowsArbitraryLoads key set to YES.
I tested it and it should work for you.

Swift / Spring: SoundPlayer.play("string.wav") not working

I try to use the Spring Framework to play a sound when a button is touched.
import UIKit
class ViewController: UIViewController {
//TODO: max, mia, beide arrays und zufallsgenerator und happy day unten mit klick gleich wave
#IBAction func soundButton(sender: AnyObject) {
let mySound : String! = "refresh.wav"
SoundPlayer.play(mySound)
}
#IBOutlet weak var imageView: SpringImageView!
#IBAction func beideButton(sender: AnyObject) {
imageView.image = UIImage(named: "b1")
imageView.animation = "zoomIn"
imageView.animate()
}
#IBAction func maxButton(sender: AnyObject) {
imageView.image = UIImage(named: "max1")
imageView.animation = "slideLeft"
imageView.animate()
}
#IBAction func miaButton(sender: AnyObject) {
imageView.image = UIImage(named: "mia1")
imageView.animation = "slideRight"
imageView.animate()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I get the following error: Cannot invoke 'play' with an argument list of type ('String')
I tried view things like as!, as String, as String!, as? String to unwrap or so but can not figure it out...
Here is the SoundPlayer class (Copyright (c) 2015 James Tang (j#jamztang.com):
import UIKit
import AudioToolbox
public class SoundPlayer: NSObject {
#IBInspectable var filename : String?
#IBInspectable var enabled : Bool = true
private struct Internal {
static var cache = [NSURL:SystemSoundID]()
}
public func playSound(soundFile:String) {
if !enabled {
return
}
if let url = NSBundle.mainBundle().URLForResource(soundFile, withExtension: nil) {
var soundID : SystemSoundID = Internal.cache[url] ?? 0
if soundID == 0 {
AudioServicesCreateSystemSoundID(url, &soundID)
Internal.cache[url] = soundID
}
AudioServicesPlaySystemSound(soundID)
} else {
println("Could not find sound file name `\(soundFile)`")
}
}
#IBAction public func play(sender: AnyObject?) {
if let filename = filename {
self.playSound(filename)
}
}
}
Judging by the code alone (I'm not familiar with Spring), what you need to do is create a SoundPlayer instance and call playSound on it (not play, which uses an internal filename for the sound to play instead of its argument), like this:
#IBAction func soundButton(sender: AnyObject) {
let mySound : String! = "refresh.wav"
let soundPlayer = SoundPlayer()
soundPlayer.playSound(mySound)
}

Swift: how to call NSSpeechRecognizer func

My speechRecognizer func doesn't seem to be called. I couldn't find anything in the documentation about calling this func.
Any idea what I might be doing wrong? Thanks in advance.
class ViewController: NSViewController, NSSpeechRecognizerDelegate {
let SR:NSSpeechRecognizer = NSSpeechRecognizer()
var commands = ["word","hello"]
override func viewDidLoad() {
super.viewDidLoad()
SR.commands = commands
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
#IBAction func Listen(sender: AnyObject) {
SR.startListening(); print("listening")
}
#IBAction func Stop(sender: AnyObject) {
SR.stopListening()
}
func speechRecognizer(sender: NSSpeechRecognizer,
didRecognizeCommand command: AnyObject?){
if (command as String == "word")
{
println("case word")
}
else if (command as String == "happy")
{
println("case happy")
}
}
}
Set the NSSpeechRecognizerDelegate to self:
SR.delegate = self

Resources