Extract row data using editActionsForRowAtIndexPath from Parse.com PFQueryTableViewController - ios

I am using PFQueryTableViewController to retrieve images from Parse.com backend. Later, I want to take a snapshot of swiped row image (using editActionsForRowAtIndexPath).
At this moment, I can retrieve object and create a action on row using editActionsForRowAtIndexPath. The action passes the retrivedObject through a prepareForSegue method to ShareViewController. Once I am on ShareViewController, I can see an image, but it is not the same I clicked to share using editActionsForRowAtIndexPath. It is either the image above or below or sometimes its the same one that i clicked.
Can anyone help me to solve this?
My code is as below:
PFQueryTableViewController
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell") as! DataViewTableViewCell!
if cell == nil {
cell = DataViewTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
}
retrivedObject = object
// Display profile image
let profileThumbnail = UIImage(named: "DProfilePicture")
cell.profilePicture.image = profileThumbnail
if let thumbnail = object?["profilePicture"] as? PFFile {
cell.profilePicture.file = thumbnail
cell.profilePicture.loadInBackground()
}
// Display main image
let initialThumbnail = UIImage(named: "DefaultImage")
cell.picture.image = initialThumbnail
if let thumbnail = object?["picture"] as? PFFile {
cell.picture.file = thumbnail
cell.picture.loadInBackground()
}
//........
}
override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
print("EDIT MODE INDEX: \(indexPath.item)")
let Share = UITableViewRowAction(style: .Normal, title: "Share") { action, index in
print("Share button tapped")
self.performSegueWithIdentifier("ShareImage", sender: self)
}
Share.backgroundColor = UIColor.blackColor()
return [Share]
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.destinationViewController is ShareViewController {
let vc = segue.destinationViewController as! ShareViewController
vc.selectedObject = retrivedObject
}
}
ShareViewController
var selectedObject: PFObject!
var sharedImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(animated: Bool) {
if selectedObject == nil {
return
}
if let file = selectedObject["picture"] as? PFFile {
file.getDataInBackgroundWithBlock({ (data, error) -> Void in
if data != nil {
self.sharedPicture.image = UIImage(data: data!)
self.screenShotMethod()
}
})
}
}
#IBAction func cancelBtn(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func shareBtn(sender: AnyObject) {
let vc = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
vc.setInitialText("Look at this great picture!")
vc.addImage(sharedImage)
self.presentViewController(vc, animated: true, completion: nil)
}
func screenShotMethod() -> UIImage {
//Create the UIImage
UIGraphicsBeginImageContextWithOptions(self.shareView.frame.size, true, 2.0 )
self.shareView.drawViewHierarchyInRect(CGRect(x: 0, y: 0, width: self.shareView.bounds.width, height: self.shareView.bounds.height), afterScreenUpdates: false)
view.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
sharedImage = image
return sharedImage
}

The problem is your use of retrivedObject. By setting retrivedObject = object it is always set to the last object (cell) displayed. It is not related to the user selection.
When you create the share action you should set
self.performSegueWithIdentifier("ShareImage", sender: index)
so that in the prepareForSegue function you can use the index (which is the sender) to get the true selected object.

Related

On long press, how do you call an ImagePreviewController: UIViewController from an objective c function instead of a contextMenuInteraction function?

This function below needs to instead go into an ObjC function
///this 1st func is in UITableViewController, the others are in UITableViewCell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? ViewControllerTableViewCell {
...}
class ViewControllerTableViewCell: UITableViewCell, UIContextMenuInteractionDelegate
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
UIContextMenuConfiguration(identifier: nil, previewProvider: {
if self.cc == interaction {
let image3 = UIImage(named:"ringy.png")
if let unwrappedImage1 = image3 {
return ImagePreviewController(image:unwrappedImage1)
}
else {
return nil
}
}
else if self.vv == interaction{
let image3 = UIImage(named:"green.png")
if let unwrappedImage1 = image3 {
return ImagePreviewController(image:unwrappedImage1)
}
else {
return nil
}
}
else {
return nil
}
})
}
Now Obj C function
#objc func didLongPress() {
///database call
if ab as! Int>0 {
/// here the part for ringy.png needs to go
} else {
/////here the part for green.png needs to go
}
}
else {
print("No data available")
}
})
}
ObjC gets handle in override function
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(didLongPress))
like?.addGestureRecognizer(longPress)
What error am I currently getting:
Non void in void function. That is if I take the ringy/image3 and green/image3 part of the code and put it is the objC function.
Update after first answer
I seem to get it working with the answer and some modifications
weak var viewController: UIViewController?
viewController?.present(previewVC, animated: true, completion: nil)
cell.viewController = self ///inside cellForRowAt
Only issue I have left is that the dimension of the ImagePreviewController are wrong/virtually full screen. They should be:
class ImagePreviewController: UIViewController {
private let imageView = UIImageView()
init(image: UIImage) {
super.init(nibName: nil, bundle: nil)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.image = image
view = imageView
let size = UIScreen.main.bounds.size
preferredContentSize = .init(width: size.width, height: size.height/1.55)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
If you want to present a new view controller, you could either perform a segue (if you work with storyboards) or do it the classic way:
#objc func didLongPress() {
var image:UIImage?
// Some database call? How to get "ab"?
// Maybe you should not use as! in the following check:
if ab as! Int>0 {
image = UIImage(named:"ringy.png")
} else {
image = UIImage(named:"green.png")
}
if let image = image {
let previewVC = ImagePreviewController(image:image)
self.present(previewVC, animated: true, completion: nil)
} else {
print("No data available")
}
}

Appending Values Consistently in Swift

EDIT: Just changing the code to show my attempt at the Singleton suggestion.
So I am attempting to create an application which would take the selection of a user from a UITableView and pass that to another UITableView. Then, it would be appended to the array in that view and presented as the new table. The idea being that users can select from multiple lists and create one list made of all their selections.
However, I am extremely new to iOS development and while I can get it to let me take the selected value and show it in the new UITableView, it only sends the one value and does not keep or append it. Meaning I can never show multiple additions to the list.
So, what I'm getting atm is the ability to select a cell, let's say "Kevin Smith", and that value gets sent to the new UITableView and shown to the user. But if I go and select another value, "John Smith", then only John shows up and Kevin is gone.
Here is my three controllers involved:
The first UITAbleView Controller
class PlayerViewController: UITableViewController {
var resultsController: NSFetchedResultsController<Player>!
let CDSPlayer = coreDataStackPlayer()
override func viewDidLoad() {
super.viewDidLoad()
tableView.allowsMultipleSelection = true
let request: NSFetchRequest<Player> = Player.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "level", ascending: true)
request.sortDescriptors = [sortDescriptor]
resultsController = NSFetchedResultsController(
fetchRequest: request,
managedObjectContext: CDSPlayer.managedContext,
sectionNameKeyPath: nil,
cacheName: nil
)
resultsController.delegate = self
do{
try resultsController.performFetch()
} catch {
print("Perform Fetch Error: \(error)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return resultsController.sections?[section].numberOfObjects ?? 0
}
override func tableView(_ _tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "PlayerCell", for: indexPath)
let selectedIndexPaths = tableView.indexPathsForSelectedRows
let rowIsSelected = selectedIndexPaths != nil && selectedIndexPaths!.contains(indexPath)
cell.accessoryType = rowIsSelected ? .checkmark : .none
let player = resultsController.object(at: indexPath)
cell.textLabel?.text = player.name
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "player2form", sender: tableView.cellForRow(at: indexPath))
}
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let action = UIContextualAction(style:.destructive, title: "Delete"){(action, view, completion) in
let player = self.resultsController.object(at: indexPath)
self.resultsController.managedObjectContext.delete(player)
do {
try self.resultsController.managedObjectContext.save()
self.errorMessage(message: "Player Character Deleted.");
completion(true)
} catch {
print("Delete Failed: \(error)")
self.errorMessage(message: "Failed to Delete Player Character.");
completion(false)
}
}
action.image = UIImage(named: "trash")
action.backgroundColor = .red
return UISwipeActionsConfiguration(actions: [action])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if let _ = sender as? UIBarButtonItem, let vc = segue.destination as? AddPlayerViewController{
vc.managedContext = resultsController.managedObjectContext
}
if let cell = sender as? UITableViewCell, let vc = segue.destination as? AddPlayerViewController{
vc.managedContext = resultsController.managedObjectContext
if let indexPath = tableView.indexPath(for: cell){
let player = resultsController.object(at: indexPath)
Service.shared.allPlayers.append(player)
vc.player = player
}
}
}
//Error Function
func errorMessage(message:String){
let alert = UIAlertController(title: "Alert", message: message, preferredStyle:UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title:"Ok", style:UIAlertActionStyle.default, handler:nil);
alert.addAction(okAction);
self.present(alert, animated: true, completion:nil);
}
}
extension PlayerViewController: NSFetchedResultsControllerDelegate{
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type{
case .insert:
if let indexPath = newIndexPath{
tableView.insertRows(at: [indexPath], with: .automatic)
}
case .delete:
if let indexPath = indexPath{
tableView.deleteRows(at: [indexPath], with: .automatic)
}
case .update:
if let indexPath = indexPath, let cell = tableView.cellForRow(at: indexPath){
let player = resultsController.object(at: indexPath)
cell.textLabel?.text = player.name
}
default:
break
}
}
The Selection view that passes the data:
class AddPlayerViewController: UIViewController {
var managedContext: NSManagedObjectContext!
var player: Player?
var selectedItems = [String]()
#IBOutlet weak var done_btn: UIButton!
#IBOutlet weak var playerInput: UITextField!
#IBOutlet weak var cancel_btn: UIButton!
#IBAction func add2combat(_ sender: UIButton) {
self.performSegue(withIdentifier: "player2combat", sender: self)
}
#IBAction func done(_ sender: UIButton) {
guard let name = playerInput.text, !name.isEmpty else {
return //Add notice user cannot save empty items
}
if let player = self.player {
player.name = name
} else {
//Set values from input to the cell
let player = Player(context: managedContext)
player.name = name
}
do {
try managedContext.save()
playerInput.resignFirstResponder()
dismiss(animated: true)
} catch {
print("Error Saving Player Character: \(error)")
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
if segue.identifier == "player2combat" {
let selectedItems : [String] = [playerInput.text!]
let otherVc = segue.destination as! CombatSceneViewController
otherVc.selectedItems = selectedItems
print(selectedItems)
}
}
#IBAction func cancel(_ sender: UIButton) {
playerInput.resignFirstResponder()
dismiss(animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
playerInput.becomeFirstResponder()
if let player = player {
playerInput.text = player.name
playerInput.text = player.name
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
And the UITableView that gets the data and tries to append it to the existing Array.
class CombatSceneViewController: UITableViewController{
var selectedItems = Service.shared.allSelectedItems
override func viewDidLoad() {
super.viewDidLoad()
let otherVC = AddPlayerViewController()
selectedItems.append(contentsOf: otherVC.selectedItems)
saveData()
loadData()
print(selectedItems)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return selectedItems.count
}
override func tableView(_ _tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "CombatCell", for: indexPath as IndexPath)
cell.textLabel?.text = selectedItems[indexPath.item]
return cell
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tableView.reloadData()
}
func saveData() {
let data = NSMutableData()
// 1
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let path = paths[0]
let file = (path as NSString).appendingPathComponent("Persistent.plist")
//2
let archiver = NSKeyedArchiver(forWritingWith: data)
archiver.encode(selectedItems, forKey: "Agents")
archiver.finishEncoding()
data.write(toFile: file, atomically: true)
}
func loadData() {
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let path = paths[0]
let file = (path as NSString).appendingPathComponent("Persistent.plist")
// 1
if FileManager.default.fileExists(atPath: file) {
if let data = NSData(contentsOfFile: file) {
let unarchiver = NSKeyedUnarchiver(forReadingWith: data as Data)
selectedItems = unarchiver.decodeObject(forKey: "Agents") as! [String]
unarchiver.finishDecoding()
}
}
}
Singleton Class:
class Service {
static let shared = Service()
var allPlayers = [Player]()
var allSelectedItems = [String]()
}
I hope I did the formatting right... this is my first post on here, be gentle^^;.
The problem is that when you go back the old player is gone because you don't keep it , you can try to create a singleton for that
class Service {
static let shared = Service()
var allPlayers = [Player]()
var allSelectedItems = [String]()
}
// keep it here
if let cell = sender as? UITableViewCell, let vc = segue.destination as? AddPlayerViewController{
vc.managedContext = resultsController.managedObjectContext
if let indexPath = tableView.indexPath(for: cell){
let player = resultsController.object(at: indexPath)
Service.shared.allPlayers.append(player) // add this line
vc.player = player
}
}
Do same logic to append selected items and when you reach final tableView access them with
Service.shared.allPlayers
Or
Service.shared.selectedItems
That way you have a persisted container for all the selected players & items accessible anyWhere inside the app

Segue not working in Swift

I'm trying to pass a certain part of an array to another view controller based on which tableview cell you click on. So when I click on the third cell for example, it'll send the array data from the third cell to the next screen. This data is an array called "todos". When I try to receive the data in the next screen, there is no data.
Code:
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EditTodo" {
print("it is")
var vc = segue.destination as! ViewController
// var indexPath = tableView.indexPathForCell(sender as UITableViewCell)
var indexPath = tableView.indexPathForSelectedRow
if let index = indexPath {
vc.todo = todos[index.row]
}
}
}
I'm not sure if it's being called at all, or what. The identifier is correct, and I'm not sure what else to do. (When I run it, the print function is not called, but I'm not even sure if it's supposed to be).
Here is the whole page of code from the 'sending data' page with the table:
import UIKit
var todos: [TodoModel] = []
var filteredTodos: [TodoModel] = []
class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
print("i think it worked...")
let defaults = UserDefaults.standard
if todos.count > 0 {
// Save what we have
let data = NSKeyedArchiver.archivedData(withRootObject: todos)
defaults.set(data, forKey: "TDDATA")
defaults.synchronize()
print("saved \(todos.count)")
} else if let storedTodoData = defaults.data(forKey: "TDDATA"),
let storedTodos = NSKeyedUnarchiver.unarchiveObject(with: storedTodoData) as? [TodoModel] {
// There was stored data! Use it!
todos = storedTodos
print("Used \(todos.count) stored todos")
tableView.reloadData()
self.tableView.tableFooterView = UIView()
}
//print([todos.first])
print("Here?: \(todos.first?.title)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override var prefersStatusBarHidden: Bool {
return true
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == searchDisplayController?.searchResultsTableView {
return filteredTodos.count
}
else {
return todos.count
}
}
// Display the cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Must use 'self' here because searchResultsTableView needs to reuse the same cell in self.tableView
let cell = self.tableView.dequeueReusableCell(withIdentifier: "todoCell")! as UITableViewCell
var todo : TodoModel
if tableView == searchDisplayController?.searchResultsTableView {
todo = filteredTodos[indexPath.row] as TodoModel
}
else {
todo = todos[indexPath.row] as TodoModel
}
//var image = cell.viewWithTag(101) as! UIImageView
var title = cell.viewWithTag(102) as! UILabel
var date = cell.viewWithTag(103) as! UILabel
//image.image = todo.image
// image = UIImageView(image: newImage)
// if image.image == nil{
// print("nilish")
// image = UIImageView(image: UIImage(named: "EmptyProfile.png"))
// }
// image.image = todo.image
// if image.image == nil{
// print("pic is nil")
// image.image = UIImage(named: "CopyEmptyProfilePic.jpg")
// }
title.text = todo.title
date.text = "\(NSDate())"
let locale = NSLocale.current
let dateFormat = DateFormatter.dateFormat(fromTemplate: "yyyy-MM-dd", options:0, locale:locale)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = dateFormat
return cell
}
// MARK - UITableViewDelegate
// Delete the cell
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete {
todos.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath as IndexPath], with: UITableViewRowAnimation.automatic)
let defaults = UserDefaults.standard
if todos.count >= 0 {
// Save what we have
let data = NSKeyedArchiver.archivedData(withRootObject: todos)
defaults.set(data, forKey: "TDDATA")
defaults.synchronize()
print("saved \(todos.count)")
} else if let storedTodoData = defaults.data(forKey: "TDDATA"),
let storedTodos = NSKeyedUnarchiver.unarchiveObject(with: storedTodoData) as? [TodoModel] {
// There was stored data! Use it!
todos = storedTodos
print("Used \(todos.count) stored todos")
}
tableView.reloadData()
}
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 80
}
// Edit mode
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
tableView.setEditing(editing, animated: true)
}
// Move the cell
func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return self.isEditing
}
// func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
// let todo = todos.removeAtIndex(sourceIndexPath.row)
//todos.insert(todo, atIndex: destinationIndexPath.row)
// }
// MARK - UISearchDisplayDelegate
// Search the Cell
func searchDisplayController(controller: UISearchDisplayController, shouldReloadTableForSearchString searchString: String?) -> Bool {
//filteredTodos = todos.filter({( todo: TodoModel) -> Bool in
// let stringMatch = todo.title.rangeOfString(searchString)
// return stringMatch != nil
//})
// Same as below
filteredTodos = todos.filter(){$0.title.range(of: searchString!)
!= nil}
return true
}
// MARK - Storyboard stuff
// Unwind
#IBAction func close(segue: UIStoryboardSegue) {
print("closed!")
tableView.reloadData()
}
// Segue
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EditTodo" {
print("it is")
var vc = segue.destination as! ViewController
// var indexPath = tableView.indexPathForCell(sender as UITableViewCell)
var indexPath = tableView.indexPathForSelectedRow
if let index = indexPath {
vc.todo = todos[index.row]
}
}
}
}
How I am receiving the data in viewDidLoad of receiving screen:
nameOfDocTF.delegate = self
nameOfDocTF.text = todo?.title
outputTextView.attributedText = todo?.desc
print(todo?.title)
//prints "nil"
As I suspected you´re missing the didSelectRowAt function which will be called when you click on a cell and from there you need to call your prepareForSegue. Implement the following function and try:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "EditTodo", sender: indexPath.row)
}
And then replace your prepareForSegue function with the following:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "EditTodo" {
let vc = segue.destination as! ViewController
if let index = sender as? Int {
vc.todo = todos[index]
}
}
}

What i should change in my code to access all the contacts instead of just 1 contact with swift?

I have a really specific problem. I want to access and present to a table view the user's contact list. The problem is that when I run the code I access only one contact from the contact list. I'm trying to think what I should change but nothing so far. Given the code below what I need to change the achieve the result that I want? Here is the code:
import UIKit
import Contacts
import AddressBook
class MasterViewController: UITableViewController {
var detailViewController: DetailViewController? = nil
var objects = [CNContact]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let addExisting = UIBarButtonItem(title: "Add Existing", style: .Plain, target: self, action: #selector(MasterViewController.addExistingContact))
self.navigationItem.leftBarButtonItem = addExisting
if let split = self.splitViewController {
let controllers = split.viewControllers
self.detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MasterViewController.insertNewObject(_:)), name: "addNewContact", object: nil)
self.getContacts()
}
func getContacts() {
let store = CNContactStore()
if CNContactStore.authorizationStatusForEntityType(.Contacts) == .NotDetermined {
store.requestAccessForEntityType(.Contacts, completionHandler: { (authorized: Bool, error: NSError?) -> Void in
if authorized {
self.retrieveContactsWithStore(store)
}
})
} else if CNContactStore.authorizationStatusForEntityType(.Contacts) == .Authorized {
self.retrieveContactsWithStore(store)
}
}
func retrieveContactsWithStore(store: CNContactStore) {
do {
let groups = try store.groupsMatchingPredicate(nil)
let predicate = CNContact.predicateForContactsInGroupWithIdentifier(groups[0].identifier)
//let predicate = CNContact.predicateForContactsMatchingName("John")
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName), CNContactEmailAddressesKey]
let contacts = try store.unifiedContactsMatchingPredicate(predicate, keysToFetch: keysToFetch)
self.objects = contacts
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
} catch {
print(error)
}
}
func addExistingContact() {
}
override func viewWillAppear(animated: Bool) {
self.clearsSelectionOnViewWillAppear = self.splitViewController!.collapsed
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func insertNewObject(sender: NSNotification) {
if let contact = sender.userInfo?["contactToAdd"] as? CNContact {
objects.insert(contact, atIndex: 0)
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
}
// MARK: - Segues
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = objects[indexPath.row]
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! DetailViewController
controller.contactItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
// MARK: - Table View
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objects.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let contact = self.objects[indexPath.row]
let formatter = CNContactFormatter()
cell.textLabel?.text = formatter.stringFromContact(contact)
cell.detailTextLabel?.text = contact.emailAddresses.first?.value as? String
return cell
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return false
}
}
This is how I currently get a list of the user's contacts and store them in a CNContact array.
let request = CNContactFetchRequest(keysToFetch: [CNContactEmailAddressesKey, CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName)])
var contacts = [CNContact]()
do {
try store.enumerateContactsWithFetchRequest(request) { contact, stop in
contacts.append(contact)
}
self.objects = contacts
NSOperationQueue.mainQueue().addOperationWithBlock({
self.tableView.reloadData()
})
} catch {
print(error)
}
I'm assuming your problem is with the line groupsMatchingPredicate(nil), where you don't pass in a predicate so it can't find any matches. Just a thought.

UICollectionView Functionality issues

I have a UICollectionViewCell on VC1, The cell contains, a image, a label, and 3 buttons.
When i click on the cell. That triggers my didSelectItemAtIndexPath to take me to an Edit item screen.
How can i access each button and relate it to the cell i am clicking on?
So if i have added 6 cells, and i click on cell 1, button 1, it takes me to a bio page with info on that person. if i click on cell 2 button 1, it brings me to same bio VC but with different info related to the cell i clicked on.
My confusion lies in where or how to set this up?
Thank you!
import UIKit
import Parse
class TrainersViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, AddNewTrainerViewControllerDelegate {
var trainers: [TrainerArray]
required init?(coder aDecoder: NSCoder) {
trainers = [TrainerArray]()
super.init(coder: aDecoder)
loadTrainerItems()
}
//connection to the collection view
#IBOutlet weak var collectionView: UICollectionView!
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("selected")
saveTrainerItems()
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return trainers.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("TrainerCell", forIndexPath: indexPath)
var buttonOne = cell.viewWithTag(10)
buttonOne = indexPath.row
let trainer = trainers[indexPath.row]
configureTrainerForCell(cell, withTrainerArray: trainer)
return cell
}
func configureTrainerForCell(cell: UICollectionViewCell, withTrainerArray trainer: TrainerArray) {
if trainer.trainerImage == nil {
let label = cell.viewWithTag(1000) as! UILabel
trainer.trainerImage = UIImage(named: "defaultImage")
label.text = trainer.name
} else {
let image = cell.viewWithTag(2000) as! UIImageView
image.image = trainer.trainerImage
let label = cell.viewWithTag(1000) as! UILabel
label.text = trainer.name
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//This method adds a new trainer to the trainer array
func addNewTrainerViewController(controller: AddNewTrainerViewController, didFinishAddingItem item: TrainerArray) {
let newRowIndex = trainers.count
trainers.append(item)
let indexPath = NSIndexPath(forRow: newRowIndex, inSection: 0)
let indexPaths = [indexPath]
collectionView.insertItemsAtIndexPaths(indexPaths)
dismissViewControllerAnimated(true, completion: nil)
saveTrainerItems()
}
func addNewTrainerViewController(controller: AddNewTrainerViewController, didFinishDeletingItem item: TrainerArray) {
if let index = trainers.indexOf(item) {
let indexPath = NSIndexPath(forRow: index, inSection: 0)
let indexPaths = [indexPath]
if let _ = collectionView.cellForItemAtIndexPath(indexPath) {
self.trainers.removeAtIndex(index)
self.collectionView.deleteItemsAtIndexPaths(indexPaths)
}
}
dismissViewControllerAnimated(true, completion: nil)
saveTrainerItems()
}
//This Method Edits a Trainer
func addNewTrainerViewController(controller: AddNewTrainerViewController, didFinishEditingItem trainer: TrainerArray) {
if let index = trainers.indexOf(trainer) {
let indexPath = NSIndexPath(forRow: index, inSection: 0)
if let cell = collectionView.cellForItemAtIndexPath(indexPath){
configureTrainerForCell(cell, withTrainerArray: trainer)
}
}
saveTrainerItems()
dismissViewControllerAnimated(true, completion: nil)
}
func addNewTrainerViewControllerDidCancel(controller: AddNewTrainerViewController) {
dismissViewControllerAnimated(true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "AddTrainer" {
let navigationController = segue.destinationViewController as! UINavigationController
let controller = navigationController.topViewController as! AddNewTrainerViewController
controller.delegate = self
} else if segue.identifier == "EditTrainer" {
let navigationController = segue.destinationViewController as! UINavigationController
let controller = navigationController.topViewController as! AddNewTrainerViewController
controller.delegate = self
if let indexPath = collectionView.indexPathForCell(sender as! UICollectionViewCell) {
controller.trainerToEdit = trainers[indexPath.row]
}
}
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
}
func documentsDirectory() -> String {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
return paths[0]
}
func dataFilePath() -> String {
return (documentsDirectory() as NSString)
.stringByAppendingPathComponent("Trainers.plist")
}
func saveTrainerItems() {
let data = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWithMutableData: data)
archiver.encodeObject(trainers, forKey: "TrainersArray")
archiver.finishEncoding()
data.writeToFile(dataFilePath(), atomically: true)
}
func loadTrainerItems() {
let path = dataFilePath()
if NSFileManager.defaultManager().fileExistsAtPath(path) {
if let data = NSData(contentsOfFile: path) {
let unarchiver = NSKeyedUnarchiver(forReadingWithData: data)
trainers = unarchiver.decodeObjectForKey("TrainersArray") as! [TrainerArray]
unarchiver.finishDecoding()
}
}
}
#IBAction func logOut(sender: AnyObject) {
let alert = UIAlertController(title: "Are You Sure You Want To Log Out?", message: "Please Enter Your Username", preferredStyle: UIAlertControllerStyle.Alert)
alert.addTextFieldWithConfigurationHandler { (textField) -> Void in
}
alert.addAction(UIAlertAction(title: "Log Out", style: UIAlertActionStyle.Default, handler: { (action) -> Void in
let textF = alert.textFields![0] as UITextField
if textF.text! != PFUser.currentUser()?.username {
self.displayGenericAlert("Incorrect Username!", message: "Please Enter a Valid Username")
} else if textF.text! == PFUser.currentUser()?.username {
PFUser.logOut()
_ = PFUser.currentUser()
self.dismissViewControllerAnimated(true, completion: nil)
}
}))
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Default, handler: { (action) -> Void in
if action == true {
self.dismissViewControllerAnimated(false, completion: nil)
}}))
self.presentViewController(alert, animated: true, completion: nil)
}
func displayGenericAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { (action) -> Void in
}))
self.presentViewController(alert, animated: true, completion: nil)
}
#IBAction func bioSegueButton(sender: AnyObject) {
}
}
You can get data from your array from index 'indexpath.row' .
You can add a tag to the button that corresponds to the index of the cell it is in. In the cellForItemAtIndexPath you would add something like...
button.tag = indexPath.row
And in the selector for the button you can access it...
func buttonSelector(sender: UIButton) {
let index = sender.tag
let trainer = trainers[index]
}
EDIT:
A more complete version of what you might do in cellForRowAtIndexPath:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("TrainerCell", forIndexPath: indexPath) as? MyCollectionViewCell
var buttonOne = cell?.button1
buttonOne?.tag = indexPath.row
let trainer = trainers[indexPath.row]
configureTrainerForCell(cell, withTrainerArray: trainer)
return cell!
}
Your collection view cell class:
class MyCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var button1: UIButton! // connect this to the button in your interface builder's collection view cell.
// Do the same for any other subviews.
}
Also in interface builder change the class of the collection view cell prototype to this new custom class.

Resources