jjjjjjjj
jjjjjjjj

Reputation: 4493

Passing data with delegates/protocols

I have a UICollectionView with a CollectionReusableView header. I want to pass a string from the collecitonview to the header, so that the header knows which data to load based on the string. I am trying to use delegates/protocols to do this, but keep getting "unexpectedly found nil while unwrapping an optional value." Here is my code:

protocol UserToQuery {
    func thisUser(x: String)
}


class Profile: UICollectionViewController {

    var ownProfile = true
    var delegate:UserToQuery?

    override func viewDidLoad() {
        super.viewDidLoad()

        if self.ownProfile == true {
            let username = PFUser.currentUser()?.username
            self.delegate!.thisUser(username!)
        }
    }
}

And here is the code for the Header view:

class ProfileHeader: UICollectionReusableView, UserToQuery {

    var id1 = String()
    var controller = Profile()


    override func awakeFromNib() {
        print(id1)
        controller.delegate? = self
    }

    func thisUser(x: String) {
        self.id1 = x
        getProfileInfo()
    }

    func getUserData() {
        // code here uses the id1 value to get data
    }

}   

My understanding of delegates/protocols is this: if you want to pass data (i.e., string), to another view, you make the view that receives the string conform to a protocol. This protocol includes a function that is used to pass the string, and when that function is called, it notifies the other view that the string is now available for use, and then you code what you want and use the string. Is that accurate?

Upvotes: 0

Views: 171

Answers (2)

keithbhunter
keithbhunter

Reputation: 12324

In ProfileHeader, you have a variable, controller, which is creating a new instance of Profile, which is NOT the Profile view controller from your storyboard. This is why self.delegate! is nil in Profile.viewDidLoad().

I am going to make the assumption that ProfileHeader is a view in the Profile view controller. In your viewDidLoad, you should set the delegate to the ProfileHeader. See the example code below (I assume an outlet for the ProfileHeader view):

EDIT: ProfileHeader is not an outlet, as mentioned in the comments. Updated my answer to reflect that.

class Profile: UICollectionViewController {

    var ownProfile = true
    var delegate:UserToQuery?

    override func viewDidLoad() {
        super.viewDidLoad()

        // Set the delegate!
        self.delegate = ProfileHeader()

        if self.ownProfile == true {
            let username = PFUser.currentUser()?.username
                // Delegate won't be nil now
                self.delegate!.thisUser(username!)
            }
        }
    }
}

As a general flow, the view controller should keep references to the view, not the other way around. So remove the controller property from your ProfileHeader view. The view shouldn't care what view controller is controlling it.

Upvotes: 1

Michaël Azevedo
Michaël Azevedo

Reputation: 3894

You have some misunderstandings about protocol/delegate, but it’s normal when you start iOS development.

First of all, why does the app crash : The variable delegate is an optional UserQuery. It’s okay for a delegate to be optional, but it’s never set in your code, so when you call :

 self.delegate!.thisUser(username!)  

you try to force unwrapping a nil variable, which results in the crash.

Protocols

Now, let’s talk about the protocol/delegate relationship.

You have an UICollectionViewController subclass, which embeds an UICollectionView object. This UICollectionView will be contains a mix of header, footer and cell. Your ProfileHeader class will thus be displayed within your UICollectionView.

In order to populate an UICollectionView, you don’t need to create your own protocol : there are already two protocols for this :

  • UICollectionViewDataSource is the main protocol to conforms to, because it allows you to populate the collection view
  • UICollectionViewDelegate is used for further customization of your tableview, i.e. customizing the appearance and handling events.

Since your Profile class inherits from UICollectionViewControlleryou don’t have to named these protocols after your class name since UICollectionViewController already conforms to these protocols as written in Apple docs

You will have to override the delegate and protocol methods in order to display some data. My advice is, before using headers and footers, to use only UICollectionViewCell objects for start easily.

By overriding the method -collectionView:numberOfItemsInSection: and - collectionView:cellForItemAtIndexPath:, you will be able to populate the collection view.

Upvotes: 0

Related Questions