provideAPIKey: should be called at most once (Swift) - ios

I'm using Google maps and places API and i'm trying to load nearby places in a tableView but everytime i come in this class
import UIKit
import MapKit
import CoreLocation
import GoogleMaps
import GooglePlaces
import Social
import AVFoundation
private let resueIdentifier = "MyTableViewCell"
extension UIViewController {
func present(viewController : UIViewController, completion : (() -> ())? = nil ){
if let presented = self.presentedViewController {
presented.dismiss(animated: true, completion: {
self.present(viewController, animated: true, completion: completion)
})
} else {
self.present(viewController, animated: true, completion: completion)
}
}
}
class CourseClass2: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
struct User {
var name: String
var images: UIImage
var type: String
}
var previuosViewTappedButtonsArray = [String]()
var locationManager:CLLocationManager?
let minimumSpacing : CGFloat = 15 //CGFloat(MAXFLOAT)
let cellWidth: CGFloat = 250
let radius = 5000 // 5km
var category : QCategoryy?
var currentLocation : CLLocationCoordinate2D?
var places: [QPlace] = []
var isLoading = false
var response : QNearbyPlacesResponse?
var rows = 0
var users = [User]()
override func viewDidLoad() {
super.viewDidLoad()
self.title = category?.name
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
determineMyCurrentLocation()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
rows = 0
insertRowsMode3()
tableView.reloadData()
category?.markView()
}
#IBAction func refreshTapped(_ sender: Any) {
rows = 0
insertRowsMode3()
tableView.reloadData()
}
func canLoadMore() -> Bool {
if isLoading {
return false
}
if let response = self.response {
if (!response.canLoadMore()) {
return false
}
}
return true
}
func loadPlaces(_ force:Bool) {
if !force {
if !canLoadMore() {
return
}
}
print("load more")
isLoading = true
NearbyPlaces.getNearbyPlaces(by: category?.name ?? "food", coordinates: currentLocation!, radius: radius, token: self.response?.nextPageToken, completion: didReceiveResponse)
}
func didReceiveResponse(response:QNearbyPlacesResponse?, error : Error?) -> Void {
if let error = error {
let alertController = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
let actionDismiss = UIAlertAction(title: "Dismiss", style: .cancel, handler: nil)
let actionRetry = UIAlertAction(title: "Retry", style: .default, handler: { (action) in
DispatchQueue.main.async {
self.loadPlaces(true)
}
})
alertController.addAction(actionRetry)
alertController.addAction(actionDismiss)
DispatchQueue.main.async {
self.present(viewController: alertController)
}
}
if let response = response {
self.response = response
if response.status == "OK" {
if let placesDownloaded = response.places {
places.append(contentsOf: placesDownloaded)
}
self.tableView?.reloadData()
} else {
let alert = UIAlertController.init(title: "Error", message: response.status, preferredStyle: .alert)
alert.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: nil))
alert.addAction(UIAlertAction.init(title: "Retry", style: .default, handler: { (action) in
DispatchQueue.main.async {
self.loadPlaces(true)
}
}))
self.present(viewController: alert)
}
isLoading = false
}
else {
print("response is nil")
}
}
func insertRowsMode2() {
tableView.beginUpdates()
for i in 0..<places.count {
insertRowMode2(ind: i, usr: places[i])
}
tableView.endUpdates()
}
func insertRowMode2(ind:Int,usr:QPlace) {
tableView.beginUpdates()
let indPath = IndexPath(row: ind, section: 0)
rows = ind + 1
tableView.insertRows(at: [indPath], with: .right)
tableView.endUpdates()
}
func insertRowsMode3() {
tableView.beginUpdates()
rows = 0
insertRowMode3(ind: 0)
tableView.endUpdates()
}
func insertRowMode3(ind:Int) {
tableView.beginUpdates()
let indPath = IndexPath(row: ind, section: 0)
rows = ind + 1
tableView.insertRows(at: [indPath], with: .right)
guard ind < places.count-1 else { return }
DispatchQueue.main.asyncAfter(deadline: .now()+0.20) {
self.insertRowMode3(ind: ind+1)
}
tableView.endUpdates()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return places.count /* rows */
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyTableViewCell
let place = places[indexPath.row]
cell.update(place: place)
if indexPath.row == places.count - 1 {
loadPlaces(false)
}
/* let user = users[indexPath.row]
cell.selectionStyle = .none
cell.myImage.image = user.images
cell.myLabel.text = user.name
cell.myTypeLabel.text = user.type */
return (cell)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
UIView.animate(withDuration: 0.2, animations: {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyTableViewCell
})
performSegue(withIdentifier: "goToLast" , sender: users[indexPath.row])
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete {
places.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
func didReceiveUserLocation(_ userLocation:CLLocation) {
currentLocation = userLocation.coordinate
loadPlaces(true)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToLast" && sender is IndexPath {
let dvc = segue.destination as! FinalClass
dvc.index = (sender as! IndexPath).row
dvc.places = places
dvc.userLocation = currentLocation
/* guard let vc = segue.destination as? FinalClass else { return }
let guest = segue.destination as! FinalClass
if let user = sender as? User {
*/
}
}
#IBAction func IndTapped(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func socialShare(_ sender: Any) {
//Alert
let alert = UIAlertController(title: "Share", message: "First share!", preferredStyle: .actionSheet)
//First action
let actionOne = UIAlertAction(title: "Share on Facebook", style: .default) { (action) in
//Checking if user is connected to Facebook
if SLComposeViewController.isAvailable(forServiceType: SLServiceTypeFacebook)
{
let post = SLComposeViewController(forServiceType: SLServiceTypeFacebook)!
post.setInitialText("First")
post.add(UIImage(named: "uround logo.png"))
self.present(post, animated: true, completion: nil)
} else {self.showAlert(service: "Facebook")}
}
let actionThree = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
//Add action to action sheet
alert.addAction(actionOne)
alert.addAction(actionThree)
//Present alert
self.present(alert, animated: true, completion: nil)
}
func showAlert(service:String)
{
let alert = UIAlertController(title: "Error", message: "You are not connected to \(service)", preferredStyle: .alert)
let action = UIAlertAction(title: "Dismiss", style: .cancel, handler: nil)
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
}
extension CourseClass2: CLLocationManagerDelegate {
func determineMyCurrentLocation() {
locationManager = CLLocationManager()
locationManager?.delegate = self
locationManager?.desiredAccuracy = kCLLocationAccuracyBest
locationManager?.requestWhenInUseAuthorization()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0] as CLLocation
manager.stopUpdatingLocation()
print("user latitude = \(userLocation.coordinate.latitude)")
print("user longitude = \(userLocation.coordinate.longitude)")
didReceiveUserLocation(userLocation)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error \(error)")
errorGettingCurrentLocation(error.localizedDescription)
}
public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse || status == .authorizedAlways {
locationManager?.startUpdatingLocation()
//locationManager.startUpdatingHeading()
} else if status == .denied || status == .restricted {
errorGettingCurrentLocation("Location access denied")
}
}
func errorGettingCurrentLocation(_ errorMessage:String) {
let alert = UIAlertController.init(title: "Error", message: errorMessage, preferredStyle: .alert)
alert.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
}
}
i get the message "error - response status" from this function
func didReceiveResponse(response:QNearbyPlacesResponse?, error : Error?) -> Void {
if let error = error {
let alertController = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
let actionDismiss = UIAlertAction(title: "Dismiss", style: .cancel, handler: nil)
let actionRetry = UIAlertAction(title: "Retry", style: .default, handler: { (action) in
DispatchQueue.main.async {
self.loadPlaces(true)
}
})
alertController.addAction(actionRetry)
alertController.addAction(actionDismiss)
DispatchQueue.main.async {
self.present(viewController: alertController)
}
}
if let response = response {
self.response = response
if response.status == "OK" {
if let placesDownloaded = response.places {
places.append(contentsOf: placesDownloaded)
}
self.tableView?.reloadData()
} else {
let alert = UIAlertController.init(title: "Error", message: response.status, preferredStyle: .alert)
alert.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: nil))
alert.addAction(UIAlertAction.init(title: "Retry", style: .default, handler: { (action) in
DispatchQueue.main.async {
self.loadPlaces(true)
}
}))
self.present(viewController: alert)
}
isLoading = false
}
else {
print("response is nil")
}
}
so looking in the console i saw this error "((null)) was false: provideAPIKey: should be called at most once" which is perhaps the cause of the problem (even if I'm not sure), i followed the google documentation guide to get the API key for the project, here is my appDelegate where there are my keys (i changed for now the numbers of the key with "My Api key")
import UIKit
import Firebase
import CoreLocation
import GoogleMaps
import GooglePlaces
import FBSDKCoreKit
import GoogleSignIn
import FBSDKShareKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
static let googleMapsApiKey = "MY API Key"
static let googlePlacesAPIKey = "MY API Key"
var window: UIWindow?
var locationManager: CLLocationManager?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
locationManager = CLLocationManager()
locationManager?.requestWhenInUseAuthorization()
GMSServices.provideAPIKey(AppDelegate.googleMapsApiKey)
GMSPlacesClient.provideAPIKey(AppDelegate.googlePlacesAPIKey)
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
IQKeyboardManager.sharedManager().enable = true
IQKeyboardManager.sharedManager().enableAutoToolbar = false
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
if GMSServices.provideAPIKey("MY API Key") {
print("good provided keys correctly")
}
else {
print("key didn't provided")
}
return true
}
someone can tell if the problem is a wrong use of the api key or if the keys are wrong or maybe the problem is another ?

Look at what you are doing here:
// once
GMSServices.provideAPIKey(AppDelegate.googleMapsApiKey)
GMSPlacesClient.provideAPIKey(AppDelegate.googlePlacesAPIKey)
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
IQKeyboardManager.sharedManager().enable = true
IQKeyboardManager.sharedManager().enableAutoToolbar = false
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
// twice!
if GMSServices.provideAPIKey("MY API Key") {
You are calling provideAPIKey twice!
I know you want to check whether the API key is provided correctly, but the correct way to do this is not to call the method twice. Instead, you should store the return value and check the return value:
// put the return value in "success"
let success = GMSServices.provideAPIKey(AppDelegate.googleMapsApiKey)
GMSPlacesClient.provideAPIKey(AppDelegate.googlePlacesAPIKey)
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
IQKeyboardManager.sharedManager().enable = true
IQKeyboardManager.sharedManager().enableAutoToolbar = false
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
// check "success"
if success {

Related

How do I display contacts when UITextField is clicked?

I have tried multiple times to create a UITextField that when clicked should show the contacts available on the device and retrieve the phone number and display it in the textfield. However I have been unable to do that. The best that I could do is to use a button to receive and display the number on a textfield. This works! How do I do the same for when the UITextField is clicked?
I'm running it on Xcode 10
private let contactPicker = CNContactPickerViewController()
override func viewDidLoad() {
super.viewDidLoad()
configureTextFields()
configureTapGesture()
phonenumber.textContentType = .telephoneNumber
}
private func configureTextFields() {
phonenumber.delegate = self
}
private func configureTapGesture(){
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(SelfTestTimer.handleTap))
viewcontact.addGestureRecognizer(tapGesture)
}
#objc private func handleTap(){
viewcontact.endEditing(true)
}
#IBAction func pbbbbb(_ sender: Any) {
contactPicker.delegate = self
self.present(contactPicker, animated: true, completion: nil)
}
}
extension SelfTestTimer: CNContactPickerDelegate {
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
let phoneNumberCount = contact.phoneNumbers.count
guard phoneNumberCount > 0 else {
dismiss(animated: true)
return
}
if phoneNumberCount == 1 {
setNumberFromContact(contactNumber: contact.phoneNumbers[0].value.stringValue)
}else{
let alertController = UIAlertController(title: "Select one of the numbers", message: nil, preferredStyle: .alert)
for i in 0...phoneNumberCount-1 {
let phoneAction = UIAlertAction(title: contact.phoneNumbers[i].value.stringValue, style: .default, handler: {
alert -> Void in
self.setNumberFromContact(contactNumber: contact.phoneNumbers[i].value.stringValue)
})
alertController.addAction(phoneAction)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: {
alert -> Void in
})
alertController.addAction(cancelAction)
dismiss(animated: true)
self.present(alertController, animated: true, completion: nil)
}
}
func setNumberFromContact(contactNumber: String) {
var contactNumber = contactNumber.replacingOccurrences(of: "-", with: "")
contactNumber = contactNumber.replacingOccurrences(of: "(", with: "")
contactNumber = contactNumber.replacingOccurrences(of: ")", with: "")
guard contactNumber.count >= 10 else {
dismiss(animated: true) {
self.presentAlert(alertTitle: "", alertMessage: "A maximum of 10 contacts allowed per session", lastAction: nil)
}
return
}
phonenumber.text = String(contactNumber.suffix(10))
}
func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
}
}
extension SelfTestTimer: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}
I want it so that when the UITextField is clicked, the contacts will appear and when one contact is selected, the number should appear in the textfield
You should use textFieldShouldBeginEditing method. Open the contacts controller in this method and return false, no need to add a gesture recogniser.

Make two different buttons create two different email subjects

I'm trying to get the two different buttons I have shown with if indexPath.section == 1 && indexPath.row == 0 and if indexPath.section == 1 && indexPath.row == 1 to have different subjects when i pull up the email. Both buttons pull up the same subject (Suggestion: ). How would I allow the program to differentiate them?
import UIKit
import Foundation
import MessageUI
class AccountViewController: UITableViewController, MFMailComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = false
self.navigationController?.view.backgroundColor = .clear
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 1 && indexPath.row == 1{
print("pressed")
let mailComposeViewController2 = configureMailController()
if MFMailComposeViewController.canSendMail() {
self.present(mailComposeViewController2, animated: true, completion: nil)
} else {
showMailError()
}
}
if indexPath.section == 1 && indexPath.row == 0 {
print("pressed")
let mailComposeViewController = configureMailController()
if MFMailComposeViewController.canSendMail() {
self.present(mailComposeViewController, animated: true, completion: nil)
} else {
showMailError()
}
}
}
//if indexPath.section == 1 && indexPath.row == 1 {
//}
func configureMailController() -> MFMailComposeViewController {
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self
mailComposerVC.setToRecipients(["SolsticeOfficialLLC#gmail.com"])
mailComposerVC.setSubject("Suggestion: ")
return mailComposerVC
}
func configureMailController2() -> MFMailComposeViewController {
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self
mailComposerVC.setToRecipients(["SolsticeOfficialLLC#gmail.com"])
mailComposerVC.setSubject("Report: ")
return mailComposerVC
}
func showMailError() {
let sendMailErrorAlert = UIAlertController(title: "Could not send email", message: "Your device could not send email", preferredStyle: .alert)
let dismiss = UIAlertAction(title: "OK", style: .default, handler: nil)
sendMailErrorAlert.addAction(dismiss)
self.present(sendMailErrorAlert, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith: MFMailComposeResult, error: Error?){
controller.dismiss(animated: true, completion: nil)
}
}
You need to add a parameter to help you:
func configureMailController(subject = "Unspecified Subject") -> MFMailComposeViewController {
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self
mailComposerVC.setToRecipients(["SolsticeOfficialLLC#gmail.com"])
mailComposerVC.setSubject(subject)
return mailComposerVC
}
Then, do as you please when configuring:
let mailComposeViewController1 = configureMailController("Specific Subject 1")
....
let mailComposeViewController2 = configureMailController("Specific Subject 2")

camera app with collection view

I am very new to XCode and Swift. Hope you guys can give me a hand here. I am having a problem that when I click the camera button, and snap a photo and click "Use Photo", the camera will be dismissed and back to the main page again.
What I actually want :
When I click "Use Photo" the main page should show the image taken just now in thumbnail size in collection view.
Camera Example
The expected output
Here is my code :
import UIKit
import CoreLocation
import Photos
import PhotosUI
import MessageUI
let albumName = "CP"
class SpeakerViewController: UIViewController,
CLLocationManagerDelegate, UIImagePickerControllerDelegate,
UINavigationControllerDelegate, UICollectionViewDataSource,
UICollectionViewDelegate, UICollectionViewDelegateFlowLayout,
MFMailComposeViewControllerDelegate {
#IBOutlet var lblLocation: UILabel!
#IBOutlet var tvNews: UITextView!
#IBOutlet var tapView: UIView!
#IBOutlet var btnAlbum: UIButton!
#IBOutlet var btnCamera: UIButton!
#IBOutlet var btnSend: UIButton!
let locationManager = CLLocationManager()
let geoCoder = CLGeocoder()
var latitude = [String]()
var longitude = [String]()
var tap : UITapGestureRecognizer! = UITapGestureRecognizer()
var assetCollection: PHAssetCollection! = PHAssetCollection()
var photoAsset : PHFetchResult<PHAsset>!
var albumFound : Bool = false
var sendLocation : String = ""
var sendContent : String = ""
var imageArray = [UIImage]()
var isCamera : Bool = false
#IBAction func btnBack(_ sender: AnyObject) {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.deleteAssets(self.photoAsset)
}, completionHandler: {(success, error) in
NSLog("Delete image => %#", (success ? "Success" : "Error"))
self.navigationController?.dismiss(animated: true, completion: nil)
})
}
override func viewDidLoad() {
super.viewDidLoad()
LocalStore.setActiveView("speaker")
self.navigationController?.navigationBar.isTranslucent = false
self.navigationController?.navigationBar.barTintColor = UIColor(red: 255/255, green: 237/255, blue: 0/255, alpha: 1.0)
if(locationManager.responds(to: #selector(CLLocationManager.requestAlwaysAuthorization))) {
locationManager.requestAlwaysAuthorization()
}
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
tap = UITapGestureRecognizer(target: self, action: #selector(SpeakerViewController.DismissKeyboard))
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", albumName)
let collection:PHFetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let first_Obj:AnyObject = collection.firstObject{
//found the album
self.albumFound = true
self.assetCollection = first_Obj as! PHAssetCollection
} else {
var albumPlaceholder:PHObjectPlaceholder!
NSLog("\nFolder \"%#\" does not exist\nCreating now...", albumName)
PHPhotoLibrary.shared().performChanges({
let request = PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: albumName)
albumPlaceholder = request.placeholderForCreatedAssetCollection
},
completionHandler: {success, error in
if(success){
print("Successfully created folder")
self.albumFound = true
let collection = PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [albumPlaceholder.localIdentifier], options: nil)
self.assetCollection = collection.firstObject as PHAssetCollection!
} else {
print("Error creating folder")
self.albumFound = false
}
})
}
NotificationCenter.default.addObserver(self,
selector: #selector(SpeakerViewController.keyboardShow(_:)),
name: NSNotification.Name.UIKeyboardDidShow,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(SpeakerViewController.keyboardHide(_:)),
name: NSNotification.Name.UIKeyboardDidHide,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(SpeakerViewController.applicationBecameActive(_:)),
name: NSNotification.Name.UIApplicationDidBecomeActive,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(SpeakerViewController.applicationBecameInactive(_:)),
name: NSNotification.Name.UIApplicationWillResignActive,
object: nil)
}
/* Start listening for iCloud user change notifications */
func applicationBecameActive(_ notification: Notification){
print("did become active notification")
if LocalStore.getPostIdMenu() != "0" {
self.navigationController?.dismiss(animated: true, completion: nil)
LocalStore.setPostIdMenu("0")
}
}
func applicationBecameInactive(_ notification: Notification){
print("did become inactive notification")
LocalStore.setPostIdMenu("0")
}
func keyboardShow(_ notification: Notification) {
print("Keyboard Show")
self.view.addGestureRecognizer(tap)
}
func keyboardHide(_ notification: Notification){
print("Keyboard Hide")
self.view.removeGestureRecognizer(tap)
}
override func viewWillAppear(_ animated: Bool) {
//fetch the photo from the collection
self.navigationController?.hidesBarsOnTap = false
self.photoAsset = PHAsset.fetchAssets(in: self.assetCollection, options: nil)
self.collectionView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
let alertView = UIAlertController(title: "出现错误", message: "无法检索您的位置", preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: "好的", style: .default){ _ in})
self.present(alertView, animated: true, completion: nil)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
latitude.removeAll()
longitude.removeAll()
let currentLocation = locations.last!
print("didUpdateToLocation: \(currentLocation)")
latitude.append(String(format: "%.8f", currentLocation.coordinate.latitude))
longitude.append(String(format: "%.8f", currentLocation.coordinate.longitude))
print("Latitude \(latitude.count), Longtitude \(longitude.count)")
geoCoder.reverseGeocodeLocation(currentLocation, completionHandler: {(placemarks, error) -> Void in
if(error == nil && placemarks!.count > 0 ) {
let placeMark = placemarks![0] as CLPlacemark
if (placeMark.addressDictionary?["Thoroughfare"] as? String) != nil {
self.lblLocation.text = "\(placeMark.thoroughfare!), \(placeMark.postalCode!), \(placeMark.administrativeArea!)"
print("Location \(placeMark.locality!)\(placeMark.country!)")
self.locationManager.stopUpdatingLocation()
self.sendLocation = self.lblLocation.text!
} else {
print("thoroughfare not found, reupdate coordinate")
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.startUpdatingLocation()
}
} else {
NSLog("%#", error.debugDescription)
}
})
}
func DismissKeyboard() {
view.endEditing(true)
}
#IBOutlet var collectionView: UICollectionView!
#IBAction func btnAlbum(_ sender: AnyObject) {
isCamera = false
if(UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.savedPhotosAlbum)) {
let imagePicker : UIImagePickerController = UIImagePickerController()
imagePicker.sourceType = UIImagePickerControllerSourceType.photoLibrary
imagePicker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)!
imagePicker.delegate = self
imagePicker.allowsEditing = false
self.present(imagePicker, animated: true, completion: nil)
}
}
#IBAction func btnCamera(_ sender: AnyObject) {
isCamera = true
if(UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.camera)) {
let imagePicker : UIImagePickerController = UIImagePickerController()
imagePicker.sourceType = UIImagePickerControllerSourceType.camera
imagePicker.delegate = self
imagePicker.allowsEditing = false
self.present(imagePicker, animated: true, completion: nil)
} else {
let alert = UIAlertController(title: "出现错误", message: "您的手机没有相机", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "好的", style: .default, handler: {(alertAction) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
}
#IBAction func btnSend(_ sender: AnyObject) {
var count : Int = 0
if(self.photoAsset != nil) {
count = self.photoAsset.count
}
if (tvNews.text != "" && count > 0) {
sendContent = tvNews.text
if (MFMailComposeViewController.canSendMail()) {
let emailTitle = "App"
let messageBody = "位置: \(sendLocation) \n\n \(sendContent) \n\n "
let toReceipients = ["test#com"]
let mc : MFMailComposeViewController = MFMailComposeViewController()
mc.mailComposeDelegate = self
mc.setSubject(emailTitle)
mc.setMessageBody(messageBody, isHTML: false)
mc.setToRecipients(toReceipients)
}
for image in self.imageArray {
let photoData : Data = UIImagePNGRepresentation(image)!
mc.addAttachmentData(photoData, mimeType: "image/png", fileName: "CP.png")
}
self.present(mc, animated: true, completion: nil)
} else {
print("No email account found")
}
} else {
let alertView = UIAlertController(title: "出现错误", message: "没有内容或照片", preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: "好的", style: .default, handler: {(alertAction) in
alertView.dismiss(animated: true, completion: nil)
}))
self.present(alertView, animated: true, completion: nil)
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
var count : Int = 0
if(self.photoAsset != nil) {
count = self.photoAsset.count
}
return count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: PhotoCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as! PhotoCollectionViewCell
let asset : PHAsset = self.photoAsset[(indexPath as NSIndexPath).item] as PHAsset
PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFill, options: nil, resultHandler: {(result, info) in
if let image = result {
cell.setThumbnailImage(image)
self.imageArray.append(image)
}
})
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "viewLargePhoto") {
if let controller:ViewPhotoViewController = segue.destination as? ViewPhotoViewController {
if let cell = sender as? UICollectionViewCell {
if let indexPath: IndexPath = self.collectionView.indexPath(for: cell) {
controller.index = (indexPath as NSIndexPath).item
controller.photoAsset = self.photoAsset
controller.assetCollection = self.assetCollection
}
}
}
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 4
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 1
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if(isCamera) {
let image = info["UIImagePickerControllerOriginalImage"] as! UIImage
PHPhotoLibrary.shared().performChanges({
let createAssetRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
let assetPlaceholder = createAssetRequest.placeholderForCreatedAsset
if let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection,
assets: self.photoAsset as PHFetchResult<PHAsset>) {
let enumeration: NSArray = [assetPlaceholder!]
albumChangeRequest.addAssets(enumeration)
}
//CORRECTION Error : App crashes
}, completionHandler: {(success, error) in
NSLog("Adding Image to Library => %#",(success ? "Success" : "Error!"))
//Relocated1
})
//CORRECTION Relocated1
picker.dismiss(animated: true, completion: nil)
} else {
let image = info["UIImagePickerControllerOriginalImage"] as! UIImage
PHPhotoLibrary.shared().performChanges({
let createAssetRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
let assetPlaceholder = createAssetRequest.placeholderForCreatedAsset
if let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection,
assets: self.photoAsset as PHFetchResult<PHAsset>) {
let enumeration: NSArray = [assetPlaceholder!]
albumChangeRequest.addAssets(enumeration)
}
}, completionHandler: {(success, error) in
NSLog("Adding Image to Library => %#",(success ? "Success" : "Error!"))
picker.dismiss(animated: true, completion: nil)
//self.navigationController?.dismiss(animated: true, completion: nil)
})
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
switch result {
case .cancelled:
let alertView = UIAlertController(title: "取消电邮", message: "您已经取消电邮", preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: "好的", style: .default, handler: {(alertAction) in
alertView.dismiss(animated: true, completion: nil)
}))
self.present(alertView, animated: true, completion: nil)
case .saved:
let alertView = UIAlertController(title: "保存电邮", message: "您已经保存电邮", preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: "好的", style: .default, handler: {(alertAction) in
alertView.dismiss(animated: true, completion: nil)
}))
self.present(alertView, animated: true, completion: nil)
case .sent:
let alertView = UIAlertController(title: "发送电邮", message: "您已经发送您的电邮", preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: "好的", style: .default, handler: {(alertAction) in
alertView.dismiss(animated: true, completion: nil)
}))
self.present(alertView, animated: true, completion: nil)
case .failed:
let alertView = UIAlertController(title: "发送失败", message: "请检查您的电邮设定", preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: "好的", style: .default, handler: {(alertAction) in
alertView.dismiss(animated: true, completion: nil)
}))
self.present(alertView, animated: true, completion: nil)
//CORRECTION default:
// break
default :
break
}
self.dismiss(animated: false, completion: nil)
}
}
Thanks in advance!

Self.tableView.reloadData not working if there is no user tap or View change

I am having an issue; I have the following code that get execute in a IBAction and as you will see I have the code to execute reloadData of the tableView. The issue is if I don't touch the screen (like a tap or change of the view) no data is displayed in the tableView but as soon as I touch the screen or move the cells (like just move them up and down) then the data appears.
The following image is the code that imports the contacts, save them in core data and loads them into an Array and execute reload data.
Here is the complete code of the ViewController where this issue is happening.
import UIKit
import CoreData
import AddressBook
class ContactsViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableviewContacts: UITableView!
//Public property that represent an array of the contacts added or copied by the user using the application
var contacts: [Contact] = []
//Public property that represent the context required to save the data in the persistance storage.
var context: NSManagedObjectContext!
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func viewWillAppear(animated: Bool) {
loadContacts()
tableviewContacts.reloadData()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contacts.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:cellContact = tableView.dequeueReusableCellWithIdentifier("cellContact", forIndexPath: indexPath) as! cellContact
let contact = contacts[indexPath.row]
cell.labelContact.text = contact.name + " " + contact.surname
return cell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the specified item to be editable.
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
var contact = contacts[indexPath.row]
var error: NSError?
var handler: HACoreDataHandler = HACoreDataHandler()
handler.context!.deleteObject(contact)
handler.context!.save(&error)
if(error != nil){
let alert = UIAlertController(title: "Error", message: error?.description, preferredStyle: UIAlertControllerStyle.Alert)
var dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction!) ->
Void in
}
alert.addAction(dismiss)
presentViewController(alert, animated: true, completion: nil)
}
contacts.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
#IBAction func importContacts(sender: AnyObject) {
let status = ABAddressBookGetAuthorizationStatus()
if status == .Denied || status == .Restricted {
// user previously denied, to tell them to fix that in settings
let alert = UIAlertController(title: "Warning", message: "We need your permission to import your contacts to giftlog.", preferredStyle: UIAlertControllerStyle.Alert)
var dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction!) ->
Void in
}
alert.addAction(dismiss)
self.presentViewController(alert, animated: true, completion: nil)
return
}
// open it
var error: Unmanaged<CFError>?
let addressBook: ABAddressBook? = ABAddressBookCreateWithOptions(nil, &error)?.takeRetainedValue()
if addressBook == nil {
println(error?.takeRetainedValue())
return
}
ABAddressBookRequestAccessWithCompletion(addressBook) {
granted, error in
if !granted {
// warn the user that because they just denied permission, this functionality won't work
// also let them know that they have to fix this in settings
let alert = UIAlertController(title: "Warning", message: "Please grant access to your contact first by granting the acess to Giftlog in the privacy phone setting ", preferredStyle: UIAlertControllerStyle.Alert)
var dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction!) ->
Void in
}
alert.addAction(dismiss)
self.presentViewController(alert, animated: true, completion: nil)
return
}
if let people = ABAddressBookCopyArrayOfAllPeople(addressBook)?.takeRetainedValue() as? NSArray {
var handler: HACoreDataHandler = HACoreDataHandler()
var error: NSError?
for person:ABRecordRef in people{
if (ABRecordCopyValue(person, kABPersonFirstNameProperty) != nil){
var contact = NSEntityDescription.insertNewObjectForEntityForName("Contact", inManagedObjectContext: handler.context!) as! Contact
contact.name = (ABRecordCopyValue(person, kABPersonFirstNameProperty).takeRetainedValue() as? String)!
contact.surname = (ABRecordCopyValue(person, kABPersonLastNameProperty).takeRetainedValue() as? String)!
var phones : ABMultiValueRef = ABRecordCopyValue(person,kABPersonPhoneProperty).takeUnretainedValue() as ABMultiValueRef
if (ABMultiValueGetCount(phones)>0){
let phoneUnmaganed = ABMultiValueCopyValueAtIndex(phones, 0)
contact.phone = phoneUnmaganed.takeUnretainedValue() as! String
}
let emails: ABMultiValueRef = ABRecordCopyValue(person, kABPersonEmailProperty).takeRetainedValue()
if (ABMultiValueGetCount(emails)>0){
let index = 0 as CFIndex
let emailAddress = ABMultiValueCopyValueAtIndex(emails, index).takeRetainedValue() as! String
contact.email = emailAddress
}
handler.context!.save(&error)
if (error != nil){
let alert = UIAlertController(title: "Error", message: error?.description, preferredStyle: UIAlertControllerStyle.Alert)
var dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction!) ->
Void in
}
alert.addAction(dismiss)
self.presentViewController(alert, animated: true, completion: nil)
} else {
self.contacts.append(contact)
self.tableviewContacts.reloadData()
} // else
} //if (ABRecordCopyValue(person, kABPersonFirstNameProperty) != nil)
} //for person:ABRecordRef in people
} // if let people
} // ABAddressBookRequestAccessWithCompletion
}
func loadContacts(){
var request = NSFetchRequest(entityName: "Contact")
let handler = HACoreDataHandler()
var error: NSError?
contacts = handler.context!.executeFetchRequest(request, error: &error) as! [Contact]
if(error != nil){
let alert = UIAlertController(title: "Error", message: error?.description, preferredStyle: UIAlertControllerStyle.Alert)
var dismiss = UIAlertAction(title: "Dismiss", style: .Default) { (alertAction: UIAlertAction!) ->
Void in
}
alert.addAction(dismiss)
presentViewController(alert, animated: true, completion: nil)
}
}
/*
// Override to support rearranging the table view.
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the item to be re-orderable.
return true
}
*/
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
}
This image is the code that sets the label of each cell of the tableView.
Finally this is the image from the storyboard file of the view and the prototype cell
Any suggestions will be appreciated.
Thanks in advance.

self.tableview.reloaddata() not being called in swift

class NewsTableViewController: UITableViewController {
#IBOutlet weak var authenticationButton: UIBarButtonItem!
var blogPosts = []
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
self.navigationItem.title = PFConfig.currentConfig().objectForKey("title") as? String
authenticationButton.enabled = true
if let authEnabled:Bool = PFConfig.currentConfig().objectForKey("adminEnabled") as? Bool {
if authEnabled {
authenticationButton.tintColor = UIView.appearance().tintColor
} else {
authenticationButton.tintColor = UIColor.darkGrayColor()
}
}
loadPosts()
//set the title
PFConfig.getConfigInBackgroundWithBlock { (var config: PFConfig!, var error: NSError!) -> Void in
if error == nil {
if let title:String = config.objectForKey("title") as? String {
self.navigationItem.title = title
}
if let authEnabled:Bool = config.objectForKey("adminEnabled") as? Bool {
if authEnabled {
self.authenticationButton.tintColor = UIView.appearance().tintColor
} else {
self.authenticationButton.tintColor = UIColor.darkGrayColor()
}
}
}
}
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 0
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return blogPosts.count
}
#IBAction func authenticate(sender: UIBarButtonItem) {
//check if enabled, and if not, get error message from config
if sender.tintColor != UIColor.darkGrayColor() {
//enabled
var authAlert = UIAlertController(title: "Authenticate", message: "Please login to access the admin page.", preferredStyle: UIAlertControllerStyle.Alert)
authAlert.addTextFieldWithConfigurationHandler({(textField: UITextField!) in
textField.placeholder = "Password"
})
authAlert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil))
authAlert.addAction(UIAlertAction(title: "Go", style: UIAlertActionStyle.Default, handler: { (goAction) -> Void in
let textField:UITextField = authAlert.textFields![0] as UITextField
let text = textField.text
self.authenticateUser(text)
}))
self.presentViewController(authAlert, animated: true, completion: nil)
} else {
//disabled
var serverMessage = PFConfig.currentConfig().objectForKey("adminEnabledMessage") as? String
var errorMessage = UIAlertController(title: "Error", message: "The Admin Console is not enabled right now. Message from server: \(serverMessage!)", preferredStyle: UIAlertControllerStyle.Alert)
errorMessage.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(errorMessage, animated: true, completion: nil)
}
}
//authHandlers
func authenticateUser(password: String) {
//get the server password
let serverPass = PFConfig.currentConfig().objectForKey("password") as? String
if password == serverPass {
//move them to the admin console
self.performSegueWithIdentifier("showConsole", sender: nil)
} else {
//error message
var errorMessage = UIAlertController(title: "Error", message: "Incorrect password, please try again.", preferredStyle: UIAlertControllerStyle.Alert)
errorMessage.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(errorMessage, animated: true, completion: nil)
}
}
func loadPosts() {
let query = PFQuery(className: "Posts")
query.findObjectsInBackgroundWithBlock { (objects, error: NSError!) -> Void in
if error == nil {
self.blogPosts = objects
self.tableView.reloadData()
} else {
var errorMessage = UIAlertController(title: "Error", message: "There was an error retrieving the posts. Please try again later. \(error.localizedDescription)", preferredStyle: UIAlertControllerStyle.Alert)
errorMessage.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(errorMessage, animated: true, completion: nil)
}
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as PostTableViewCell
// Configure the cell...
let currentPost:PFObject = blogPosts.objectAtIndex(indexPath.row) as PFObject
let title = currentPost.objectForKey("title") as String
cell.titleLabelC.text = title
let gmtFormatter:NSDateFormatter = NSDateFormatter()
gmtFormatter.dateFormat = "E MMM d # hh:mm a"
let dateString = gmtFormatter.stringFromDate(currentPost.createdAt)
cell.detailsLabelC.text = dateString
return cell
}
Here is my code for the UITableView that I added to my storyboard. For some reason, when I call self.tableView.reloadData() in loadPosts(), it won't update the table and simply won't call cellForRowAtIndexPath. Any idea why?
Your TableViewDataSource always returns 0 for the number of sections but you need at least 1. Just remove that DataSource function because it is 1 by default.
This error about multithreading.
UITableView - it is UI element. UI works only in main thread. That means that reloadData also works only in main thred.
In your code query.findObjectsInBackgroundWithBlock is async method. So reloadData will not work in this method.
You should call it in main thread through GCD.
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})

Resources