I have a collection view with some cells representing a contact (their data has a phone number and name) and I am trying to add the contact to the iPhone contacts. I have created a segue from a button called "add contact" that is inside the CollectionViewCell to a navigation controller, and set its identifier as "ADD_CONTACT".
In the storyboard, my segue has a navigation controller with no root view controller.
in prepareToSegue of the view controller that delegates my UICollectionView I wrote this code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == ADD_CONTACT {
let dest = segue.destination as! UINavigationController
if let cell = sender as? SBInstructionCell {
if cell.isContact {
let newContact = CNMutableContact()
if let phone = cell.instructionBean?.contactAttachment?.phoneNumber{
newContact.phoneNumbers.append(CNLabeledValue(label: "home", value: CNPhoneNumber(stringValue: phone)))
}
if let name = cell.instructionBean?.contactAttachment?.contactName {
newContact.givenName.append(name)
}
let contactVC = CNContactViewController(forNewContact: newContact)
contactVC.contactStore = CNContactStore()
contactVC.delegate = self
dest.setViewControllers([contactVC], animated: false)
}
}
}
}
this results with a black screen.
How can this be fixed? I want to see the CNContactViewController
Eventually I solved this in a different approach using Closures.
In my UICollectionViewCell
I added this var:
var closureForContact: (()->())? = nil
Now on my button's action in the same cell I have this func:
#IBAction func addContactTapped(_ sender: UIButton) {
if closureForContact != nil{
closureForContact!()
}
}
Which calls the function.
In my CollectionView in cell for item at index path, I set the closure like this:
cell.closureForContact = {
if cell.isContact {
let newContact = CNMutableContact()
if let phone = cell.instructionBean?.contactAttachment?.phoneNumber{
newContact.phoneNumbers.append(CNLabeledValue(label: "home", value: CNPhoneNumber(stringValue: phone)))
}
if let name = cell.instructionBean?.contactAttachment?.contactName {
newContact.givenName.append(name)
}
let contactVC = CNContactViewController(forNewContact: newContact)
contactVC.contactStore = CNContactStore()
contactVC.delegate = self
contactVC.allowsEditing = true
contactVC.allowsActions = true
if let nav = self.navigationController {
nav.navigationBar.isTranslucent = false
nav.pushViewController(contactVC, animated: true)
}
}
}
This worked perfectly. I learned that for navigating from a cell, it is best to use closures.
Related
So I am trying to make an Painting app that can store the data that user gives (such as name of the painting, artist of the painting, year of the painting and image of the painting) and shows in a table view. I have 2 view controllers, first one is called ViewController that has a table view for showing the data (only name of the painting for the table view cell) and the second one is called DetailedVC which is for entering and saving the data, also showing the details of the data. In the second view controller I added 3 text fields, 1 image view that enables the user to go the photo library and a save button. I wrote this in my DetailedVC script for when the user taps one of the elements from the table view in my ViewController:
override func viewDidLoad() {
super.viewDidLoad()
if chosenPainting != "" {
savebutton.isHidden = true //Trying to hide the save button here!!
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Paintings")
let idString = chosenPaintingId?.uuidString
fetch.predicate = NSPredicate(format: "id = %#", idString!)
fetch.returnsObjectsAsFaults = false
do {
let results = try context.fetch(fetch)
if results.count > 0 {
for result in results as! [NSManagedObject] {
if let name = result.value(forKey: "name") as? String {
nameText.text = name
}
if let artist = result.value(forKey: "artist") as? String {
artistText.text = artist
}
if let year = result.value(forKey: "year") as? Int {
yearText.text = String(year)
}
if let imageData = result.value(forKey: "image") as? Data {
let imageD = UIImage(data: imageData)
imageView.image = imageD
}
}
}
} catch {
print("error")
}
} else {
savebutton.isEnabled = false
nameText.text = ""
artistText.text = ""
yearText.text = ""
}
I get the information from my ViewController like this:
#objc func AddButtonTapped(){
selectedPainting = ""
performSegue(withIdentifier: "toDetailedVC", sender: self)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedPaintingId = ids[indexPath.row]
selectedPainting = names[indexPath.row]
performSegue(withIdentifier: "toDetailedVC", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toDetailedVC" {
let destination = segue.destination as! DetailedVC
destination.chosenPainting = selectedPainting
destination.chosenPaintingId = selectedPaintingId
}
}
It works just fine below the savebutton.isHidden = true. I don't understand why the button is not disappearing. Everything else works correctly, text field's texts turn into the information that user gave but, the button is standing still right in the bottom. And even the savebutton.isEnabled = false is working. I thought there must be a problem with connection between my script and my storyboard so I deleted and did it again, but it didn't work again. I must have doing something wrong, can you help me about this?
I've tried passing data backward from my unwind segue in a number of ways. It seems like the data is not getting sent or its getting sent after viewDidLoad() so the label I'm trying to set isn't getting updated. The unwind segue is working, and below I use prepare for segue with some success to change the title of the previous view controller to 'new title', but the last line isn't setting nbaRotoHome.player to 'new player name'.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "BuyStatsTapPager" {
let nav = segue.destination as! UINavigationController
let buyStatsTapPager = nav.viewControllers[0] as! BuyStatsTabPager
buyStatsTapPager.selectedPlayerBuyStats = selectedPlayer
buyStatsTapPager.buyStatsRef = self
}
if segue.identifier == "unwindToViewController1" {
var viewControllers: [UIViewController] = mainNavigationController.viewControllers as [UIViewController];
if(viewControllers.count == 3){
viewControllers.remove(at: viewControllers.count-2)
mainNavigationController?.viewControllers = viewControllers
}
let enteredContestViewController = viewControllers[viewControllers.count-1]
enteredContestViewController.title = "new title"
self.presentingViewController?.dismiss(animated: true, completion: nil)
let nbaRotoHome = segue.destination as! NBARotoHome
nbaRotoHome.player = "new player name"
}
Back in my previous view controller I have
#IBAction func prepareForUnwind(segue: UIStoryboardSegue) {
}
And after looking at this question
Passing data with unwind segue
I've also tried getting the data this way in the previous view controller
#IBAction func prepareForUnwind(segue: UIStoryboardSegue) {
if let sourceViewController = segue as? BuyStats {
playerNameLabel.text = sourceViewController.playerName
}
}
If I need to add more detail to what I'm trying to do please ask and I will edit. I wanted to ask the question but I am having trouble formulating.
It seems like the data is not getting sent or its getting sent after viewDidLoad() so the label I'm trying to set isn't getting updated.
In an unwind segue you are returning to an already created viewController, so viewDidLoad happened ages ago before you segued to the other viewController.
If you're using segues, you should not be mucking with the array of viewControllers in the navigationController or calling dismiss. The unwind segue will do all of that. Just get the destination in prepare(for:sender:) and set the data:
if segue.identifier == "unwindToViewController1" {
let nbaRotoHome = segue.destination as! NBARotoHome
nbaRotoHome.player = "new player name"
}
or in your prepareForUnwind get the source and read the data:
In this line you are missing .source. Change:
if let sourceViewController = segue as? BuyStats
to:
if let sourceViewController = segue.source as? BuyStats
I have a UITableView that uses an NSFetchedResultsController to fetch my data for the UITableView. When a user selects a row in the UITableView, I have an edit modal view slide up with two UITextFields for the user to edit the data that was in the UITableViewCell. What I am trying to accomplish is setting the text of each UITextField with the text that was in the UITableViewCell from Core data. I am doing this so that the user does not have to re-type their data just to make an edit.
The issue is when I try to set the text for each UITextField in prepareForSegue, I am getting a fatal error " unexpectedly found nil while unwrapping an Optional value". I know that the data is there, so I am unsure exactly why it is finding nil. I followed this stackoverflow post to write my prepareForSegue function correctly when using an NSFetchedResultsController. This is my code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "edit" {
if let inputController = segue.destinationViewController as? QAInputViewController {
let uri = currentDeck!.objectID.URIRepresentation()
let objectID = managedContext.persistentStoreCoordinator?.managedObjectIDForURIRepresentation(uri)
inputController.currentDeck = managedContext.objectWithID(objectID!) as? Deck
if let indexPath = tableView.indexPathForCell(sender as! DisplayTableViewCell) {
let data = fetchedResultsController.objectAtIndexPath(indexPath) as? Card
inputController.questionInput.text = data?.question
inputController.answerInput.text = data?.answer
}
}
}
}
Thank you for your help.
I found that my the crash was happening because my destination view controller's view was not loaded yet when trying to assign text to each UITextField.
So I added two variables of type String in my destination view controller to hold the data from my prepareForSegue function instead of trying to assign the text from the selected UITableView row directly to the UITextField and then in viewWillAppear I assigned those variables to each UITextField.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "edit" {
if let inputController = segue.destinationViewController as? QAInputViewController {
let uri = currentDeck!.objectID.URIRepresentation()
let objectID = managedContext.persistentStoreCoordinator?.managedObjectIDForURIRepresentation(uri)
inputController.currentDeck = managedContext.objectWithID(objectID!) as? Deck
if let indexPath = tableView.indexPathForCell(sender as! DisplayTableViewCell) {
let data = fetchedResultsController.objectAtIndexPath(indexPath) as? Card
inputController.selectedQuestion = data!.question
inputController.selectedAnswer = data!.answer
}
}
}
}
In my destination view controller:
var selectedQuestion: String = ""
var selectedAnswer: String = ""
override func viewWillAppear(animated: Bool) {
questionInput.text = selectedQuestion
answerInput.text = selectedAnswer
}
I have a managedObject that is being passed from 1 view controller to another the first pass works fine but when I try to pass the next object after the relationship has been set it doesn't send anything and comes back as either nil or if I try to use other methods comes back with a syntax error. The code I am using for the view controllers is as follows
View Controller 1, The first object set:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
switch identifier {
case "popOver":
if let VC = segue.destinationViewController as? ClassDeckNameViewController
{
if let ppc = VC.popoverPresentationController {
VC.modalPresentationStyle = UIModalPresentationStyle.Popover
ppc.permittedArrowDirections = UIPopoverArrowDirection.Any
ppc.delegate = self
}
VC.classSave = (sender as! ClassSelection)
}
default: break
}
}
}
#IBAction func buttonPriest(sender: AnyObject) {
let entity = NSEntityDescription.entityForName("ClassSelection", inManagedObjectContext: classMOC!)
let newObject = ClassSelection(entity: entity!,insertIntoManagedObjectContext: classMOC)
newObject.classname = "Priest"
var error: NSError?
if let err = error {
println(err)
} else {
classMOC?.save(&error)
self.performSegueWithIdentifier("popOver", sender: newObject)
}
}
This passes the object without problem to the second view controller but this is the one that won't pass any further to the final presenting controller offering the user the final selections for their "Deck":
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showCardSelection" {
let detailVC: CardSelectionViewController = segue.destinationViewController as! CardSelectionViewController
detailVC.passedDeckObject = (sender as! Deck)
}
}
#IBAction func enterButton(sender: AnyObject) {
let entityDescription = NSEntityDescription.entityForName("Deck",inManagedObjectContext: managedObjectContext!)
let storeDeck = Deck(entity: entityDescription!,insertIntoManagedObjectContext: managedObjectContext)
storeDeck.deckname = usersDeckName.text
storeDeck.classSelected = classSave!
var error: NSError?
managedObjectContext?.save(&error)
if let err = error {
status.text = err.localizedFailureReason
} else {
usersDeckName.text = ""
status.text = "Deck Saved"
self.performSegueWithIdentifier("showCardSelection", sender: storeDeck)
}
}
I made passedDeckObject a variable of type Deck? in the final view controller to set the final relationship methods I know I am doing something wrong but I am unsure what! Any help with this would be amazing!
This looks to be a misconfiguration issue where the segue is being triggered directly in the storyboard rather than calling your code. As such the sender is a button rather than the new entity instance you're expecting.
To fix, disconnect the segue in the storyboard and connect (if it isn't already) the button to your action method in the view controller.
I've been struggling with this one for a while. The following code runs fine when build for ios7, but when build for ios8 just crashes. Any of these got deprecated on ios8?
Not sure if I am doing wrong by pushing a segue from a tab bar controller to a navigation controller.
If I comment my var moveVC I can transit to the HobbieFeedViewController, but then I do not pass any values to the next segue.
If I remove the navigation controller from HobbieFeedViewController the segue stops working.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
println("SENDER->\(sender)")
let cell = sender as! HobbiesTableViewCell
var cellTitle = String()
//unwrap safelly
// if let object = cell.textLabel?.text
if let object = cell.cellTitle.text
{
cellTitle = object
println("CELLTITLE->\(cellTitle)")
}
// if not
else
{
println("Could not get cellTitle")
}
// call segue
if (segue.identifier == "hobbieFeed")
{
var selectedRowIndex = self.myTableView.indexPathForSelectedRow()
// var moveVC: HobbieFeedViewController = segue.destinationViewController as! HobbieFeedViewController
//
// moveVC.moveId = selectedRowIndex!.row
// moveVC.selectedHobbie = cellTitle
println("PREPARE ROW->\(selectedRowIndex)")
println("PREPARE HOBBIE->\(cellTitle)")
}
}