collection view cells fetch data - ios

I am new to collection view. I want to retrieve data from CoreData for collection view cell. I know how to retrieve for table view cell but it failed when I use similar way to fetch for collection view. Here are my functions from CoreDataHelper and ViewController class
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
if identifier == "displayCellDetail" {
print("Task View cell tapped")
CollectionViewCoreDataHelper.retrieveTasks()
let indexPath = collectionView.indexPathsForSelectedItems!
let task = tasks[indexPath.row]
let TaskSettingViewController = segue.destination as! ViewController
TaskSettingViewController.task = task
} else if identifier == "addTask" {
print("+ button tapped")
}
}
}
static func retrieveTasks() -> [Tasks] {
let fetchRequest = NSFetchRequest<Tasks>(entityName: "Tasks")
do {
let results = try managedContext.fetch(fetchRequest)
return results
} catch let error as NSError {
print("Could not fetch \(error)")
}
return []
}

Try this :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
if identifier == "displayCellDetail" {
print("Task View cell tapped")
CollectionViewCoreDataHelper.retrieveTasks()
let cell = sender as? YourCellName //Cell from which this segue is being performed
let indexPath = self.collectionView!.indexPathForCell(cell)
let task = self.tasks[indexPath.item] //Downcast to type of task
let objTaskSettingVC = segue.destination as! TaskSettingViewController
objTaskSettingVC.tasks = task
}
else if identifier == "addTask" {
print("+ button tapped")
}
}
}

For passing the data from one viewController to other is here
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
if identifier == "displayCellDetail" {
print("Task View cell tapped")
CollectionViewCoreDataHelper.retrieveTasks()
let cell = sender as UICollectionViewCell
let indexPath = self.collectionView!.indexPathForCell(cell)
let task = self.tasks[indexPath.row] as [ToDo]
let objTaskSettingVC = segue.destination as! TaskSettingViewController // ViewController in which you want to send the data
objTaskSettingVC.tasks = [task] //tasks is your variable which is having same type and defined in your TaskSettingViewController
}
else if identifier == "addTask" {
print("+ button tapped")
}
}
}

Related

PersonalityQuiz guided app in swift fundamentals

I've got problem with some additional challenges. I need to filter an array of type Question by some property and then pass it into next View Controller via segue. I've done this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let sender = sender as? UIButton else {return}
if sender == quiz3Button {
let vc = segue.destination as? QuestionViewController
vc?.correctQuestions = questions.filter { question in
return question.quiz == .animals
}
} else if sender == quiz4Button {
let vc = segue.destination as? QuestionViewController
vc?.correctQuestions = questions.filter { question in
return question.quiz == .cars
}
}
}
#IBAction func quiz3ButtonTapped(_ sender: UIButton) {
performSegue(withIdentifier: "animals", sender: sender)
}
#IBAction func quiz4Button(_ sender: UIButton) {
performSegue(withIdentifier: "cars", sender: sender)
}
Filtration works but it doesn't pass value to next View Controller. I declared variable in QuestionViewControler like that
var correctQuestions: [Question] = []
But when I need to access it I get error "Index out of range". So I figured that its empty..
Segues been made from buttons to VC
Ok. I've got it. The NavigationController was the problem here. Added into function push through NC and it worked ;) so closed I think
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let sender = sender as? UIButton else {return}
if sender == quiz3Button {
let destinationViewController = segue.destination as? UINavigationController
let questionViewController = destinationViewController?.viewControllers.first as! QuestionViewController
questionViewController.correctQuestions = questions.filter { questions in
return questions.quiz == .animals
}
} else if sender == quiz4Button {
let destinationViewController = segue.destination as? UINavigationController
let questionViewController = destinationViewController?.viewControllers.first as! QuestionViewController
questionViewController.correctQuestions = questions.filter { questions in
return questions.quiz == .cars
}
}
}

How to perform a segue if child does not exist in Firebase

I would like to check if a user has been banned from a group before to let him have access to the group chat and then perform a segue toward the next view controller.
For that I use exist(). When the child exists, the code works perfectly but when the child does not exist, nothing happens.
I tried to change blockedUserDB.observe(.childAdded, with: { (snapshot) in for blockedUserDB.observeSingleEvent(of: .value without success. I also tried to perform the segue in putting performSegue(withIdentifier: "toConversationControler", sender: Any?.self) in another function triggered when the node does not exist ...
Here is my code
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selectedGroupID = "\(groupArray[indexPath.row].documentID)"
// Check if user blocked
let uid = Auth.auth().currentUser!.uid as String
// 1 - set breakpoint here
let blockedUserDB = self.ref2.child("blocked").child(self.selectedGroupID).child(uid)
blockedUserDB.observe(.childAdded, with: { (snapshot) in
// 2 - does this print?
print("snapshot.exists() ==", snapshot.exists())
if snapshot.exists(){
// 3 - does this print?
print("true rooms exist")
let snapshotValue = snapshot.value as! String
print("\(snapshotValue) the value")
self.blockUserCheck = snapshotValue
print(self.blockUserCheck)
if self.blockUserCheck == "true" {
SPAlert.present(message: "You have been banned from this group")
}
else
{
print("performSegue!")
self.performSegue(withIdentifier: "toConversationControler", sender: Any?.self)
print("selected group \(self.selectedGroupID)")
}
}
else
{
// 4 - does this print?
print("snapshot.exists() was false!")
self.performSegue(withIdentifier: "toConversationControler", sender: Any?.self)
}
})
}
And here is how I prepared for segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let identifier = segue.identifier else {
assertionFailure("Segue had no idientifier")
return
}
if identifier == "toConversationControler" {
let vc = segue.destination as! ConversationViewController
vc.finalGroup = self.selectedGroupID
vc.groupName = self.selectedGroupName
vc.city = self.finalCity
vc.language = self.selectedLanguage
vc.info = self.selectedInfo
}
else if identifier == "toNewConvVC" {
let newConvVC = segue.destination as! NewGroupViewController
newConvVC.city = self.finalCity
}
else {
assertionFailure("Did not recognize storyboard identifier")
}
}
Thank you for your help.

Pass data depends on the button in tableView cell

I have a TableView where I display all my data and each cell might have 1-2 buttons. I read many topics and understand how to add target for each button through my ViewController. Since these buttons will be forwarded to the same VC and display images, I have the following code. In my TableViewCell subclass I have 2 buttons
class CODetailsTicketCell: UITableViewCel {
var onButtonTapped: (() -> Void)? = nil
#IBAction func firstBtnTapped(_ sender: UIButton) {
if let onButtonTapped = self.onButtonTapped {
onButtonTapped()
}
print("First button was pressed")
}
#IBAction func secondBtnTapped(_ sender: UIButton) {
if let onButtonTapped = self.onButtonTapped {
onButtonTapped()
}
print("Second button was pressed")
}
}
In my ViewController in cellForRowAt indexPath I have the following code
let message = messages[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "COTicketsCell", for: indexPath) as? CODetailsTicketCell {
cell.configureCell(openTickets: message)
cell.onButtonTapped = {
self.performSegue(withIdentifier: "toImageVC", sender: message)
}
return cell
In order to pass the data through segue I use the following code in prepareForSegue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toImageVC" {
let navigationController = segue.destination as? UINavigationController
if let targetController = navigationController?.topViewController as? ImageVC {
if let data = sender as? OpenTicketsData {
targetController.loadImageURL = URL(string: data.firstImageUrl)
}
}
}
}
Everything is working FINE but I can't check for button tag in prepareForSegue. Basically, currently both buttons send the same data
targetController.loadImageURL = URL(string: data.firstImageUrl)
How can I pass data based on the button pressed? I tried to do something like this but seems it's wrong and not working.
let button = sender as? UIButton
if let data = sender as? OpenTicketsData {
if button?.tag == 1 {
targetController.loadImageURL = URL(string: data.firstImageUrl)
} else if button?.tag == 2 {
targetController.loadImageURL = URL(string: data.secondImageUrl)
}
}
You can either separate it into 2 different events or
class CODetailsTicketCell: UITableViewCell {
var onButtonTapped: ((_ sender: UIButton) -> Void)? = nil
#IBAction func firstBtnTapped(_ sender: UIButton) {
if let onButtonTapped = self.onButtonTapped {
onButtonTapped?(sender)
}
print("First button was pressed")
}
#IBAction func secondBtnTapped(_ sender: UIButton) {
if let onButtonTapped = self.onButtonTapped {
onButtonTapped(sender)
}
print("Second button was pressed")
}
}
In your assignment of the onButtonTapped, remember to add [weak self] if you ever use self to avoid the retain cycle.
cell.onButtonTapped = { [weak self] sender in
if sender.tag == 1 {
// Do something
} else {
// Do other thing
}
}

Swift 3, Successfully passed data but var returns nil when used

So here is my code from VC1 and passing the data to VC2.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedArtist = artists[indexPath.item]
performSegue(withIdentifier: "artistToArtSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "artistToArtSegue" {
let artCollectionController = ArtCollectionController()
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
}
}
These codes here in VC2 will print the data
class ArtCollectionController: UICollectionViewController {
var artist = Artist() {
didSet{
print(artist.artistId ?? "did not work")
print(artist.name ?? "what name?")
}
}
var selectedArtist = Artist()
but when I use the the variable in these following test codes in VC2. They return a nil.
func fetchArtForArtist() {
guard let artistId = selectedArtist.artistId else {return}
print(artistId)
let fanRef = FIRDatabase.database().reference().child("art_ref").child(artistId)
fanRef.observeSingleEvent(of: .childAdded, with: { (snapshot) in
let artId = snapshot.key
print(artId)
// let dataRef = FIRDatabase.database().reference().child(artId)
// dataRef.observe(.value, with: { (snapshot) in
// let dictionary = snapshot.value as? [String: AnyObject]
// //let art =
// }, withCancel: nil)
}, withCancel: nil)
}
#IBAction func testButton(_ sender: UIBarButtonItem) {
print(selectedArtist.name ?? "no name")
print(12345)
}
override func viewDidAppear(_ animated: Bool) {
selectedArtist = artist
print(artist.name ?? "non")
print(selectedArtist.artistId ?? "no id")
}
override func viewDidLoad() {
super.viewDidLoad()
fetchArtForArtist()
selectedArtist = artist
print(artist.name ?? "non")
print(selectedArtist.artistId ?? "no id")
}
Im doing this in storyBoard. Im even using 2 vars seeing if there is a difference. I dont understand why the data is successfully passed to VC2 to a couple variables but when the variable is used it returns a nil. Please help.
The other responses are good, but I prefer a slightly different approach:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.destination {
case let artCollectionController as ArtCollectionController:
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
case let otherViewController as OtherViewController:
//Code for some other case
}
}
By using a switch statement, you have a prepareForSegue that will handle multiple different segues cleanly.
The case let construct is a cool trick that only executes that case if the variable in the switch can be case to the desired type. If it can be cast, it creates a local variable of the desired type.
I prefer deciding what code to execute based on the class of the destination view controller because it's less fragile than using the segue identifier. If you forget to set the segue identifier, or add a second segue later to the same type of view controller, or have a typo in the name of the identifier, that code won't work. If you have a typo in your class name, though, the compiler throws an error.
Because you set the artist property on a new instance of ArtCollectionController, which is destroyed upon the exit of the prepareForSegue function:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "artistToArtSegue" {
let artCollectionController = ArtCollectionController() // created
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
// destroyed here
}
}
Try this instead:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "artistToArtSegue",
let artCollectionController = segue.destination as? ArtCollectionController
{
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "artistToArtSegue" {
let artCollectionController = segue.destination as! ArtCollectionController
artCollectionController.artist = selectedArtist
artCollectionController.selectedArtist = selectedArtist
}
}
try this, you are creating one more ArtCollectionController instead of passing data to segue one

Is it possible to pass a enum through segue in swift

I have this prepareForSegue in my first ViewController with a enum in it
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
enum TypeOfSegue {
case Edit, Create
}
if let DestViewController: DetailViewController = segue.destinationViewController as? DetailViewController where segue.identifier == "EditItem" {
let edit = TypeOfSegue.Edit
}
if let DestViewController: DetailViewController = segue.destinationViewController as? DetailViewController where segue.identifier == "CreateNewItem" {
let create = TypeOfSegue.Create
}
}
I want to pass the constants edit or create to my DetailViewController. If possible, how can I do this and under what variable will the TypeOfSegue be saved in DetailViewController?
You can define a mode property in your DetailViewController
class DetailViewController:UIViewController {
var mode: TypeOfSegue!
}
Next you can populate it
enum TypeOfSegue {
case Edit, Create
}
class ListViewController:UIViewController {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destViewController: DetailViewController = segue.destinationViewController as? DetailViewController where segue.identifier == "EditItem" {
destViewController.mode = .Edit
}
if let destViewController: DetailViewController = segue.destinationViewController as? DetailViewController where segue.identifier == "CreateNewItem" {
destViewController.mode = .Create
}
}
}
Update
You can also implement the prepareForSegue method this way
enum TypeOfSegue: String, CustomStringConvertible {
case Edit = "EditItem", Create = "CreateNewItem"
var description: String { return self.rawValue }
}
class ListViewController:UIViewController {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
guard let
destViewController = segue.destinationViewController as? DetailViewController,
identifier = segue.identifier,
segueType = TypeOfSegue(rawValue: identifier) else { fatalError("Wrong segue") }
destViewController.mode = segueType
}
}

Resources