2

I need to refresh my collectionView when user returns to that VC because what he/she did in the detailVC has affect on the previous VC data. I tried collectionView.reloadData() in both viewDidLoad() and viewDidAppear() of my VC has the collectionView in it. And It came up that when user taps the 'Back' in detailVC both viewDidLoad() and viewDidAppear() do not work. So, I tried to call one of them in detailVC with instantiate the firstVC(which has the collectionView) then I got an runtime error which said collectionView is nil. Any thoughts? (BTW, the segue between them is ShowPush, and I can not change it because I have to have the transition of this segue in my app.)

Here is the firstVC:

class SkillsController: UIViewController{

    @IBOutlet weak var collectionView: UICollectionView!

    var TAGS: [TAG] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        let nib = UINib(nibName: "TagCell", bundle: nil)
        collectionView.register(nib, forCellWithReuseIdentifier: "tagCell")
        self.sizingCell = (nib.instantiate(withOwner: nil, options: nil) as NSArray).firstObject as! TagCell?
        self.loadMore()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("back to skills")
        self.TAGS = TagManager.shared.tagList
        collectionView.reloadData()
    }

}

TAGS is my data which is stored in Realm database.

Here is the detailVC:

class SeeSelectedController: UICollectionViewController {

    var TAGS: [TAG] = []

    @IBOutlet weak var layout: FSQCollectionViewAlignedLayout!

    override func viewDidLoad() {
        super.viewDidLoad()

        if currentTab.shared.isSkill {
            self.title = "Selected Skills"
            //init tags
            let list = RealmManager.shared.skills
            if let list = list {
                for element in list {
                    TAGS.append(TAG(n: element.value!, iS: true))
                }
            }
            collectionView?.reloadData()
        }else{
            self.title = "Selected Needs"
            //init tags
            let list = RealmManager.shared.needs
            if let list = list {
                for element in list {
                    TAGS.append(TAG(n: element.value!, iS: true))
                }
            }
            collectionView?.reloadData()
        }

        let nib = UINib(nibName: "TagCell", bundle: nil)
        collectionView?.register(nib, forCellWithReuseIdentifier: "tagCell")
    }

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let item = TAGS[indexPath.row].name!
        let currentState = TAGS[indexPath.row].isSelected!
        TAGS[indexPath.row].isSelected = currentState ? false:true
        if currentState {
            print("deselect")
            //remove from realm
            RealmManager.shared.deleteItemFromList(type: getTypeOfTag(isSkill: currentTab.shared.isSkill), item: item)
        }else{
            print("select")
            //add to realm
            RealmManager.shared.addItemToList(type: getTypeOfTag(isSkill: currentTab.shared.isSkill), item: item)
        }
        if currentTab.shared.isSkill {
            let VC: SkillsController = storyboard?.instantiateViewController(withIdentifier: "SkillsController") as! SkillsController
            VC.viewDidAppear(true)
        }
        collectionView.reloadData()
        //addd
    }

}

So how it is working? in the SkillsVC user can select some tags from a pool, in the detailVC which is SeeSelecteVC he/she can drop selected tags. It is constantly changing in the Realm as you can see. The problem when user has dropped some tags in detailVC and press the Back button, the dropped tags are still looking as selected in SkillsVC. However when if user goes another VC and comes back to SkillsVC (by this way the viewDidLoad() is gonna work) the dropped tags are seems to be unselected. That's all.

Faruk
  • 2,269
  • 31
  • 42
  • `viewDidAppear()` is the right place to `collectionView.reloadData()`. ut before, you have to update the data used by the collectionView as well, which I assume you did not. – shallowThought Nov 28 '16 at 15:48
  • No, I am constantly updating the data, but in `detailVC` the data is meant to be updated. that is why it exists. When user decides to go back to `firstVC` data has already updated but I can not trigger the function to reload `collectionView` – Faruk Nov 28 '16 at 15:51
  • you show your code, I think the issue's reason you did not show – aircraft Nov 28 '16 at 15:55
  • ok the code is on its way – Faruk Nov 28 '16 at 15:57

1 Answers1

1

If what you are looking for is just to reload on back button

What you can do is create your own custom UIBarButtonItem that will make you navigate backwards from your "detail view controller". What you should do next after adding your own back button is add an IBAction for UIBarBUttonItem and pop your "detail view controller".

Right before you do this, you should create a delegate that will be executed before the popping happens that will reload your UICollectionView.

The following is not the best way to achieve what you want: In your didSelectItem for your second view controller, you are creating a new view controller here and you shouldn't force call viewDidAppear. Since you are creating a new UIViewController, you are not referencing the previous UIViewController that you came from and soo your UICollectionView is nil.

if currentTab.shared.isSkill {
     //remove the below lines and call the delegate here
     let VC: SkillsController = storyboard?.instantiateViewController(withIdentifier: "SkillsController") as! SkillsController
     VC.viewDidAppear(true)
}
collectionView.reloadData()

What you should be doing is: You should use delegates to send callbacks to previous view controllers or perform actions. To create a delegate-

Using the first approach (using your own back button)-

protocol delegateVC{

    func reloadCollectionView()
}

class SeeSelectedController: UICollectionViewController{
    //add this inside this class
    var delegate : delegateVC?
    ...

   //implement your IBAction for back button and inside it-
    ...  {

        self.delegate.reloadCollectionView()
   }
}

OR the second approach i pointed out (Just change your didSelectItem and it will reload the collectionView, no need to fret about back button at all and save the hassle, i strongly recommend this approach)

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let item = TAGS[indexPath.row].name!
        let currentState = TAGS[indexPath.row].isSelected!
        TAGS[indexPath.row].isSelected = currentState ? false:true
        if currentState {
            print("deselect")
            //remove from realm
            RealmManager.shared.deleteItemFromList(type: getTypeOfTag(isSkill: currentTab.shared.isSkill), item: item)
        }else{
            print("select")
            //add to realm
            RealmManager.shared.addItemToList(type: getTypeOfTag(isSkill: currentTab.shared.isSkill), item: item)
        }
        if currentTab.shared.isSkill {
           self.delegate.reloadCollectionView()
        }
    }
}

And in your first view controller-

func reloadCollectionView(){
     collectionView.reloadData()
}

Note: In your prepareForSegue remember to set the delegate of your detail view controller to be your first view controller

Rikh
  • 4,078
  • 3
  • 15
  • 35
  • thank for your answer. I made a little research and some say `NotificationCenter` might be useful (http://stackoverflow.com/a/25921822/1404324). what do you think about that solution? I liked yours more but I need more help to implement it as I do not have any experiences with `Delegates` – Faruk Nov 28 '16 at 17:33
  • Do read up on delegates as they are quite important when it comes to iOS. See the example i've written, it is quite simple. Protocol is essentially how you go about using delegates. You can use `NotificationCenter` but it will require you to pass dictionary in case you need to pass arguments and removing observers and what not. Do some research as to what your personal preference is and what is better and where, don't take my word for it. There are some instances when you need notifications as well. There are plenty of tutorials regarding delegates in swift as well :) – Rikh Nov 28 '16 at 17:56