PHPicker delegate error: PHPickerViewControllerDelegate doesn't respond to picker:didFinishPicking - photokit

In WWDC20 apple introduced PHPicker, the replacement for UIImagePickerController.
I have a wrapper NSObject class witch handles all the configuration and presentation of image picker controller, now I'm replacing the implementation to support iOS14.
Even if I set the delegate to be self I get the error:
[Picker] PHPickerViewControllerDelegate doesn't respond to picker:didFinishPicking:
I think it checks on the parent view controller, that indeed it's not implementing the delegate methods but the wrapper does.
Here is my example code:
import Foundation
import PhotosUI
class myPicker: PHPickerViewControllerDelegate{
func openFrom(parent:UIViewController!) {
var config:PHPickerConfiguration! = PHPickerConfiguration()
config.selectionLimit = 1
config.filter = nil
let pickerViewController:PHPickerViewController! = PHPickerViewController(configuration:config)
pickerViewController.delegate = self //<---
parent.present(pickerViewController, animated:true, completion:nil)
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion:nil)
for result:PHPickerResult in results {
let itemProvider:NSItemProvider = result.itemProvider
print(itemProvider)
}
// ...do something with images...
}
}
Using it...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let mypicker = myPicker()
mypicker.openFrom(parent: self)
}
What do you suggest?

The problem is that Cocoa Objective-C cannot introspect your class. Change
class myPicker: PHPickerViewControllerDelegate
To
class myPicker: NSObject, PHPickerViewControllerDelegate

You need to make "class myPicker" as a singleton class. Because you should make sure delegate not to be nil. Also, you can change this instance = nil in the didFinishPicking method.

Related

how to change value in struct globally in swift

So I have an api request that requests a bunch of data from a fake api url, the data I am getting is being put on a placeholder, I just want to have a global variable to be able to use that array of codable data in my collectionviews.
struct productsList{
static var itemsList = [ProductItem]()
}
func getProducts() {
storeRepo
.getAllProducts()
.subscribe { result in
productsList.itemsList = result
for item in productsList.itemsList{
print(item.category)
}
} onError: { error in
print(error.localizedDescription)
}.disposed(by: disposeBag)
}
func printReuslt() {
for i in productsList.itemsList{
print(i.id)
}
}
note that it's not printing the printResult() but it's looping inside of the .subscribe
note that i am using Moya as well as RXswift
What you're looking for is called a Singleton. Swift makes this extremely easy to do. Basically, the short and sweet is that the struct you create, initializes itself as a property of itself. Anytime you access (In this example) APIHandler.shared you'll get a reference to the only single object, which has your other properties dataObj1 and someObj2 from this example.
class APIHandler {
let shared = APIHandler()
var dataObj1: YourObj?
var someObj2: YourObj2?
init() {
self.someObj1 = yourMethodCall()
self.someObj2 = someCalculation()
}
}
This is how you access it from another class. BE CAREFUL you can access APIHandler.someObj which would result in a null reference exception if you don't have an object created, so when doing this always access the shared property.
class MainClass {
let apiHandler: APIHandler?
override func viewDidLoad(...) {
super.viewDidLoad(...)
apiHandler = APIHandler.shared
}
}

How can i pass Label from viewController to another one without using segue?

Hi im new in swift and im trying to build a store application, i already build it but i have a problem with only one thing which is , i want to pass the data ( label, image, price label ) to another viewController without using segue. I tried all the method ( delegate, notificationCenter, closure ) but i didn't solve it.
this is a picture for new project i create it to explain what i want to do exactly .
i hope i get the solution because i search alot for it maybe for months but i didn't solve my problem :(
this is my code
passingViewController
import UIKit
extension passingDataViewController : textDelegate {
func sendText(withText: String) {
Label.text = withText
}
}
class receivingDataViewController: UIViewController {
#IBOutlet var label2: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
receivingDataViewController
import UIKit
extension passingDataViewController : textDelegate {
func sendText(withText: String) {
Label.text = withText
}
}
class receivingDataViewController: UIViewController {
#IBOutlet var label2: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
You didn't explain how you're creating the new controllers, but here's a simple example that just uses a tab controller. This makes use of a shared singleton to pass around the delegate, but if you're creating each controller programatically, you could update the delegate when you create each new instance.
For this example, I have created a singleton that contains the delegate
import Foundation
class SharedSingleton : NSObject
{
var updateDelegate : UpdateDelegate?
static let sharedInstance = SharedSingleton()
}
And then we define the protocol you're using.
In the passing view, you can define it like this
protocol UpdateDelegate
{
func updateDisplay(text : String)
}
and you use it (in the passing view like this
var sharedInstance = SharedSingleton.sharedInstance
#IBAction func cmdGo(_ sender: Any) {
sharedInstance.updateDelegate!.updateDisplay(text: txtInput.text!)
}
You define the receiving view to use the update protocol
class ReceivingViewController: UIViewController, UpdateDelegate {
and set up the delegate
var sharedInstance = SharedSingleton.sharedInstance
override func viewDidLoad() {
super.viewDidLoad()
// setup the delegate through the shared instance
sharedInstance.updateDelegate = self
}
and then implement the update method
func updateDisplay(text: String) {
lblDisplay.text = text
}
That's it. You may need to update your implementation to change how you share the delegate pointer, but that's the process.

Updating array value using find object in the background is swift

I'm trying to retrieve data from Parse using findObjectsInbackground and store it in an array. I've already created outside the scoop of viewDidLoad().
I managed retrieving and printing the data, but they are not stored in the array and it keeps being empty!
I used self.articalsTableView.reloadData() but unfortunately the array myArray is still empty.
Any help please, this has been confusing me for two days!
import UIKit
import Parse
class FirstViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var articalsTableView: UITableView!
var myArray = [PFObject]()
override func viewDidLoad() {
super.viewDidLoad()
let query = PFQuery(className: "Gateshead")
query.findObjectsInBackground {(objects, error) -> Void in
if error != nil {
print(error)
} else {
if let articals = objects {
for artical in articals {
// print (articles) THIS IS WOKING WELL
self.myArray.append(artical)
self.articalsTableView.reloadData()
}
}
}
}
}
}
findObjectsInback is using a background thread and you should dispatch to mainThread whenever if you want to access UIKit stuff in this case updating self.articalsTableView.
Also self.articalsTableView.reloadData() should be called at the end (not in the loop). This to prevent a race condition on self.myArray being accessed by the main-thread (to update self.articalsTableView ) and by the background-thread (to append artical)
for artical in articals {
// print (articles) THIS IS WOKING WELL
self.myArray.append(artical)
}
DispatchQueue.main.async {
self.articalsTableView.reloadData()
}

Is it possible to display a MKClusterAnnotation callout?

Note: I am using iOS11s native mapview annotation clustering.
In a situation where annotations are still clustered at max zoom, in what manner can we show a callout?
I'm showing a pop-over type view to display a list of annotations at the cluster, but calling selectAnnotation isn't enough to show a callout for an annotation that is "clustered".
"Something" is selected, but no callout is shown. By something, I just mean that my didDeselect view method is called after I touch the mapview.
I ran through the same problem. Seems that they didn't think carefully in that case. You must select the MKClusterAnnotation instead the MKAnnotation that is clustered but it's not simple to get there.
on iOS11 there's a property on MKAnnotationView called cluster that as the documentation states is:
If non-nil this is the annotation view this view is clustered into.
So in my MKAnnotationView subclass I override the setSelected method and with a weak reference to the mapView you must select the cluster one:
//You have weak reference to mapView
weak var mapView: MKMapView?
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if #available(iOS 11.0, *) {
if selected, let cluster = cluster {
// Deselect the current annotation (Maybe this step is not required, didn't check it)
if let annotation = annotation {
mapView?.deselectAnnotation(annotation, animated: false)
}
// Select the cluster annotation
if let clusterAnnotation = cluster.annotation {
mapView?.selectAnnotation(clusterAnnotation, animated: true)
}
}
}
}
Its actually quite simple. The map view doesn't bother showing a callout if the assigned MKMarkerAnnotationView is not set to show callouts through .canShowCallout and also if there are no accessories on the view (that's the important one). If those two conditions are not met then the map can show the title and subtitle on the pin itself. So, here is all you have to do:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if annotation is MyAnnotationConformingClass {
let annotation = annotation as! MKAnnotation
let view = MKAannotationView(annotation: annotation, reuseIdentifier: "pinReUserId")
view.canShowCallout = true
view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
return view
}
if annotation is MKClusterAnnotation {
let annotation = annotation as! MKClusterAnnotation
let view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "ClusterResuseId")
view.canShowCallout = true
view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
return view
}
return nil
}
By giving the cluster's MKMarkerAnnotationView an accessory and also allowing show callouts, the callout will then be shown. If you remember with older SDKs, the map would not show a callout if you had no title and subtitle set.

Swift error Trying to Pass Array to Another Class

So I am probably doing this all wrong but I want to make a shopping list app and I have two view controllers, one with the list and one with a textbook and button to add to the list but I can't figure out how to get the array to the other class. I have this code so far and the error I get is
Instance member 'list' cannot be used in type 'mainViewController'
And this is my code
import UIKit
class mainViewController: UIViewController {
var list:[String] = [""]
#IBOutlet weak var item: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.hideKeyboardWhenTappedAround()
// Do any additional setup after loading the view.
}
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(mainViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
#IBAction func AddToList(sender: UIButton) {
list.append(item.text!)
}
static func getList(inout Array: [String]) -> [String] {
return list
}
}
Any help much appreciated, please keep it positive though and keep in mind I am very new to this forum and swift. Thanks!
The error message is very clear that you are trying to access an instance field in type context (without instance).
To fix it, just remove static in getList function.
PS: Please follow Swift naming conventions to capitalize your class name

Resources