I want to use protocols. I have a collection view and collection view cells which belongs to this collection view. I have a button in this cell. I want to call a method in collectionview class (not cell class) when user taps to this button.
I am defining my cell class with following code:
protocol testPro {
func replyClicked()
}
class MessageGalleryViewControllerCellCollectionViewCell: UICollectionViewCell {
var test1: testPro?
// ...
}
Then when user taps to button system calling following method in cell class.
func replyAction(sender:UIButton!)
{
test1?.replyClicked()
}
And My collection view class:
class ReceivedPhotosViewController: UIViewController,UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, testPro {
func replyClicked() {
print("clicked")
}
}
But I can't see any log like "clicked". So it is not working. Where is the problem?
You are not assigning your test1 to your cell. In your cellForItemAtindexPath, put cell.test1 = self after instantiating the cell.
Related
this should be straight forward but i m finding hard time in understanding.
i have a class
class Field:NSObject {
var name:String?
var answer:String?
.................
}
Now i also have a viewcontroller in which there is a tableview. I am using Field array as datasource for this tableview. i am passing relavent object from Field array to my custom cell class (see below) which have a UItextfield .
cell.field = self.fields[indexPath.row]
What i want is that when text inside UItextfield is changed this should update our main Field array. Also custom cell class should have fresh copy of Field instance. I hope i have made it clear, please ask if you didn't understand anything. Much obliged.
here is cell class code
class SpeechCell: UITableViewCell, UITextFieldDelegate {
public var field:Field?{
didSet{
self.lblField.text = self.field?.name
self.txtAnswer.placeholder = self.field?.name
}
}
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
self.field?.answer = textField.text!
self.commandDelegate?.didUpdateField(field: self.field!, textField: textField)
}
override func prepareForReuse() {
self.txtAnswer.text = self.field?.answer
}
here is code for tableview
func didUpdateField(field: Field, textField: UITextField) {
self.fields[textField.tag].answer = textField.text!
}
You should use custom delegate method to notify the view controller about the changes in the textfield. In the custom delegate method you should update the fields array. You should change the field in the cell too before calling the custom delegate method.
You should call this delegate method in the textFieldDidChangeText.
So I have TableViewCell's that are being populated from 2 Dicitionaries in my view controller.
var categories : [Int : [String : Any]]!
var assignments : [Int: [String : Any]]!
I have a UiTextField in my cell that the user is supposed to be able to edit. I then want to be able to change the values of certain keys in that dictionary-based off what the user changes and re-display the table with those changes. My main problem is that I don't know how I will be able to access theese variables from within my cell. I have a method in my view controller that takes the row that the text field is in, along with the value of the textField, and updates the dictionaries. What I need is to be able to instantiate the view controller that the cell is in but I need the original instance that already has values loaded into the categories and assignments Dictionaries. If you have any other ideas on how I could accomplish this please post.
You can use delegate for sharing cell-data to your VC:
protocol YourCellDelegate() {
func pickedString(str: String)
}
class YourCell: UITableViewCell {
var delegate: YourCellDelegate! = nil
#IBOutlet weak var textField: UITextField!
.....//some set-method, where you can handle a text
func textHandle() {
guard let del = delegate else { return }
del.pickedString(textField.text)
}
.....
}
And usage in your VC: When you create cell, set its delegate self:
...
cell.delegate = self
...
and sure you VC supported your Delegate Protocol:
class YourVC: UIViewController, YourCellDelegate {
}
And now, you MUST implement protocol method:
class YourVC: UIViewController, YourCellDelegate {
....
func pickedString(str: String) {
}
....
}
All times, when you use textHandle() in your cell, pickedString(str: String) activates in yourVC with string from textField.
Enjoy!
I am trying to perform a segue from another class via a class function.
I have a class called MyTableViewController. In that class I have constructed a view controller of the type AnswerViewController. A segue to this view controller is supposed to occur when a condition in the Extension : MyCell is met. The problem that I am having is that the function showNextView is not being called.
I have read posts on both Perform Segue From Another Swift File via a class function and Perform Segue from another class with helper function, but both of these create a segue before constructing the view controller (which I cannot do because I am not using storyboards and do not actually have segues, only pushViewController).
class MyTableViewController: UITableViewController {
//Construct View Controller
let answerViewController = AnswerViewController()
//Create goToNextView function which will be called in extension MyCell
func goToNextView(){
navigationController?.pushViewController(answerViewController, animated: true)
}
}
extension MyCell: YSSegmentedControlDelegate{
func segmentedControl(_ segmentedControl: YSSegmentedControl, willPressItemAt index: Int) {
tagToIndex[actionButton.tag] = index
print(tagToIndex)
//Condition To Be Met
if tagToIndex == [0:1,1:0,2:1]{
//Access function goToNextView from MyTableViewController
func showNextView(fromViewController : MyTableViewController){
fromViewController.goToNextView()
}
}
}
}
How do I call the showNextView function so that the segue occurs?
Thanks,
Nick
You can't do this that way. Your showNextView function is nested inside segmentedControl(_, willPressItemAt) - this means it is not accessible outside of it. You generally shouldn't use nested functions.
To solve your issue you should create a delegate for your cell and inform your view controller that an action has occured.
A simple example :
protocol MyCellDelegate: class {
func myCellRequestedToOpenAnswerVC(cell: MyCell)
}
class MyCell {
weak var delegate: MyCellDelegate?
// rest of your inplementation
}
Then, change segmentedControl(_, willPressItemAt) to :
func segmentedControl(_ segmentedControl: YSSegmentedControl, willPressItemAt index: Int) {
tagToIndex[actionButton.tag] = index
print(tagToIndex)
//Condition To Be Met
if tagToIndex == [0:1,1:0,2:1]{
self.delegate?.myCellRequestedToOpenAnswerVC(cell: self)
}
}
The last part happens in MyTableViewController - first, in your cellForRow method assign the view controller as delegate, something like this - cell.delegate = self, and make the view controller conform to MyCellDelegate:
extension MyTableViewController: MyCellDelegate {
func myCellRequestedToOpenAnswerVC(cell: MyCell) {
self.goToNextView()
}
}
Now, whenever the condition is met, your view controller will get informed about it and be able to act accordingly.
If you are not familiar with protocols and delegation pattern, I highly recommend reading through the docs, as it is something used extensively in CocoaTouch.
I'm constructing a tabView with a collectionView as one of the tabs. In the tabView, I have a button that takes a Photo and adds it to the collection view.
The problem I'm facing is reloading the collection view from the TabView, because I'm getting the following error:
"Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'UICollectionView must be initialized with a non-nil layout parameter'"
In the TabView I'm calling this function:
PhotoListController().reloadData()
And in the PhotoListController, that function is:
func reloadData() {
getData() //function that goes through an array with the data of the images
self.collectionView!.reloadData()
}
When I had everything in the same class, it worked pretty well, but now I can't fix the problem :(
If anybody can give me a hand it would be very much appreciated.
Well I've finally found a solution within this topic:
How to reload data in a TableView from a different ViewController in Swift
and this one:
Swift: Reload collection view data from another view class with swift
The solution is related to use a NotificationCenter
in this code: PhotoListController().reloadData() you create a new copy of PhotoListController class, but you need to apply to the same instance of PhotoListController class that was already been created. I guess you need to apply the delegate pattern.
protocol PhotoListDelegate: AnyObject {
func reloadData()
}
class PhotoListController : UIViewController, PhotoListDelegate {
var tableView = TableView()
var collectionView = UICollectionView()
override func viewDidLoad() {
tableView.photoListDelegate = self
}
func reloadData() {
getData()
self.collectionView.reloadData()
}
func getData() {
// your code to get data
}
}
class TableView: UITableView {
weak var photoListDelegate : PhotoListDelegate?
// then in TableView Class:
func photoCatched() {
self.photoListDelegate?.reloadData()
}
}
secondTBLVControllert <- FirstTableVC <- UITableVC
In secondTBLVControllert i am not getting tableview and its cell, in this class i have used static cells and style is group, i have set datasource and delegate in IB and code also... Im using storyboard.
and i have tried by implementing and without implementing delegate and datasource methods.
import Foundation
import UIKit
class secondTBLVControllert : FirstTableVC {
override func viewDidLoad() {
super.viewDidLoad()
}
}
i am getting empty view.. i am not getting my designs