I have this in my storyboard.
ViewController -> Tableview -> Custom cell
the custom cell has inside a collection view.
Inside my ViewController i have some filter buttons that i want to filter the array data of the collection view and reload it also.
So I tried to make a delegate method with the following code
protocol ReloadTheDataDelegate: class {
func reloadTheCV()
}
class NearMeViewController: UIViewController {
weak var delegate: ReloadTheDataDelegate?
#IBAction func anAction(_sender : AnyObject){
requests.weekendEventData.sort() { $0.realDate < $1.realDate }
delegate?.reloadTheCV()
}
}
class WeekendTableViewCell: UITableViewCell, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UICollectionViewDelegate, ReloadTheDataDelegate {
#IBOutlet public weak var weekendCV: UICollectionView!
func reloadTheCV() {
print("done") //never printed
weekendCV.reloadData()
}
override func awakeFromNib() {
super.awakeFromNib()
if let myViewController = parentViewController as? NearMeViewController {
myViewController.delegate = self
}
weekendCV.register(UINib(nibName: "WeekendCollectionViewCell", bundle: nil),
forCellWithReuseIdentifier: "phoneweekendcell")
weekendCV.delegate = self
weekendCV.dataSource = self
}
}
And the extension that i take the ViewController
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
Could anyone explain why the function reloadTheCV() is never called ?
NearMeViewController delegate (ReloadTheDataDelegate ) is nil. Due to that its not printing. You didn't set the delegate and this approach is too complex also.
So, Instead of approaching with protocol concept. Simply you can reload the cell's collection view with the following manner.
Add the following function on your NearMeViewController. And call whenever you need reload the collection view.
func reloadCellCV() {
for cell in tableView.visibleCells {
if let cell = cell as? WeekendTableViewCell {
DispatchQueue.main.async {
cell.weekendCV.reloadData()
}
}
}
}
Related
I have a table on which a cell is made using .xib. There are two views on this cell. When I click on each view, and I set the image and title for another viewController. I made a protocol for this, but I just can’t get this data on another viewController because my delegate = nil.
In this class, I set properties.
сlass DataTableViewCell: UITableViewCell {
var dataDelegete: DataAccountCellDelegete? = nil
#objc func accountViewTaped() {
dataDelegete?.navigationBarIcon(image: UIImage(named: "icon") ?? UIImage())
dataDelegete?.navigationBarTitle(title: "title")
}
#objc func settingsViewTaped() {
dataDelegete?.navigationBarIcon(image: UIImage(named: "icon") ?? UIImage())
dataDelegete?.navigationBarTitle(title: "title")
}
}
protocol DataAccountCellDelegete {
func navigationBarIcon(image: UIImage)
func navigationBarTitle(title: String)
}
In this class, I get and want to set these properties, but my delegate = nil
class AccountViewController: UIViewController {
var dataVC = DataTableViewCell()
override func viewDidLoad() {
super.viewDidLoad()
dataVC.delegete = self
}
}
extension AccountViewController: DataAccountCellDelegete{
func menuNavigationBarIcon(image: UIImage) {
menuNavigationBar.addImage(image)
}
func menuNavigationBarTitle(title: String) {
menuNavigationBar.addTitle(title)
}
}
How do I declare a delegate correctly?
The view hierarchy should be like
ViewController -> TableView -> TableViewCell
So, ViewController has the reference of TableView & TableView has the reference of the cell. So the data passing should be reversed.
TableViewCell -> TableView -> ViewController
as vadian said ,Cells are reused and being created in cellForRowAt. The variable dataVC is useless here.
I have a viewController that inherits from UITabBarController .
I am trying to override the selectedIndex variable so that I can get notified when it gets changed with the didSet like the code below.
override var selectedIndex: Int {
didSet {
refreshTabBar()
}
}
The problem is that that function is not getting called when tabs get changed and I need to know why.
PS: I do not want to call it from the didSelect delegate method.
Thanks.
As apple documentation says about selectedIndex:
This property nominally represents an index into the array of the
viewControllers property.
So it's computed property which returns firstIndex of selectedViewController from viewControllers.
And on setting it changes the selectedViewController.
Use some other UITabBarController property instead. F.e:
override var selectedViewController: UIViewController? {
didSet {
print(selectedIndex)
refreshTabBar()
}
}
Set selectedIndex programmatically to call the function
class FirstViewController: TabViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.selectedIndex = 1
}
}
class TabViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
}
override var selectedIndex: Int{
didSet {
refreshTabBar()
}
}
}
Can anyone help me to give reason to use delegate/protocol oriented or get superview, as I know swift use protocol oriented on code but for grab parent view or controller we still can use get superview like
Get superview example:
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
Use delegate example:
protocol SomeDelegate {
func didClick()
}
class Child {
var delegate: SomeDelegate?
}
What Pros and Cons to use delegate or get superview ?
Example for parentView:
class Cell {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.parentViewController.view.makeToast("Something !")
}
}
Example for delegate:
class Parent: SomeDelegate {
func didClick() {
self.view.makeToast("Something !")
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableview.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? Cell
cell.delegate = self
return cell
}
}
class Cell {
var label: UILabel
var delegate: SomeDelegate?
func configure() {
label.addGestureRecognizer(UILongPressGestureRecognizer(
target: self,
action: #selector(copyAction(_:))
))
}
#objc private func copyAction(_ sender: UILongPressGestureRecognizer) {
guard let delegate = self.delegate else {
return
}
delegate.didClick()
}
}
Delegate is preferred, not the superview. some reasons below
A view added in stack is not always retained in memory after its addition. especially when no strong reference maintained (this differs when view added from XIB or SB). So in this case calling superview might sometime crash with an unrecognized selector sent on some random instance.
One can create a view and never add to another view. ex for sake of removing you might comment just addsubview line leaving other code as is. At this time also the superview is nil.
Usage of custom views under uicontrols with own reusable view stack like Collectionview,TableView etc. would change superviews in runtime. so not always guaranteed to call same superview instance.
I am trying to create a protocol delegate between two UICollectionViewController. with the code I have I don't get any errors or warnings however, I cannot get the delegate to work. What am I missing?
Second Collection View
public protocol LettersCollectionViewDelegate: class {
func DidSelectLetter(collectioView: UICollectionView,letter: Character, resultString:String)
}
class LettersCollectionView: UICollectionViewController {
// DELEGATE
weak var delegate: LettersCollectionViewDelegate?
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell : UICollectionViewCell = collectionView.cellForItemAtIndexPath(indexPath)! as! LetterCellView
delegate?.DidSelectLetter(collectionView, letter: "T", resultString:"TestString")
}
}
First Collection View
class AnswerCollectionView: UICollectionViewController {
let lettersView = LettersCollectionView()
override func viewDidLoad() {
super.viewDidLoad()
self.lettersView.delegate = self
}
}
extension AnswerCollectionView: LettersCollectionViewDelegate {
func DidSelectLetter(collectioView: UICollectionView, letter: Character, resultString: String) {
print(letter)
}
}
UPDATE
You need your delegate to be the instance of the AnswerCollectionView that is embedded in your root view controller. Similarly, you need to set the delegate on the LettersCollectionView instance that is in the root view. let lettersView = LettersCollectionView() creates a new instance.
You can get the required references in prepareForSegue in your root view controller. You need to give the two embded segues in your storyboard identifiers, so you can identify them.
class ViewController: UIViewController {
var lettersView: LettersCollectionView?
var answersView: AnswersCollectionView?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "lettersSegue" {
let lettersView = segue.destinationViewController as? LettersCollectionView
} else if segue.identifier = "answersSegue" {
let answersView = segue.destinationViewController as? AnswersCollectionView
}
self.lettersView?.delegate = self.answersView
}
i edited my question , because set textfield maybe can't be simple, need references so this is my code, but still have issue :
this code for TableViewController :
import UIKit
protocol PaymentSelectionDelegate{
func userDidSelectPayment(title: NSString?)
}
class PopPaymentTableViewController: UITableViewController {
var delegate : PaymentSelectionDelegate!
override func viewDidLoad() {
super.viewDidLoad()
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath?) {
self.dismissViewControllerAnimated(true){
if self.delegate != nil{
if let ip = indexPath{
var payItem : PayMethod
payItem = self.myList[ip.row] as! PayMethod
var title = payItem.paymentName
self.delegate.userDidSelectPayment(title)
}
}
}
}
}
and for code TransactionViewController :
import UIKit
class TransactionViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, PaymentSelectionDelegate, UITextFieldDelegate, UIPopoverPresentationControllerDelegate{
#IBOutlet var paymentTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
func resign(){
paymentTextField.resignFirstResponder()
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
if (textField == paymentTextField){
resign()
performSegueWithIdentifier("seguePayment", sender: self)
}
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None
}
func userDidSelectPayment(title: NSString?) {
paymentTextField.text = title as! String
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "seguePayment"{
var VC = segue.destinationViewController as! UINavigationController
var controller = VC.popoverPresentationController
if controller != nil{
controller?.delegate = self
var paymentVC = PopPaymentTableViewController()
paymentVC.delegate = self
}
}
}
}
this issue is: variable delegate in TableViewController like seems always nil, so cant set value
NB : sorry i edited totally my question, because so many answer say cant be set textfield just like that
The context of where your TransactionViewController is, is not completely clear, but you are instantiating a new ViewController. If you want to refer to an existing ViewController, this is not the instance you are using here. If you want to create a new one and show it after your didSelectRowAtIndexPath, you have to make sure, that the TextField is instantiated in the init-Method of your ViewController. As you are not instantiating it from a Storyboard, it seems that your TransactionViewController is created programmatically. Probably you are setting the TextField only in viewDidLoad or something else after the init().
You are trying to set text to paymentTextField which is still no initialized.
For this you have to set text to paymentTextField in viewDidLoad method in TransactionViewController.
remove transVC.paymentTextField.text = title from didSelectRowAtIndexPath
Add it in TransactionViewController's viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
self.paymentTextField.text = self.payName
}
It is not correct way to do that. You have not initialised text field and trying to set it's text. First initialise text field:
transVC.paymentTextField = UITextField()
Then try to do something.