Reputation: 4493
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
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
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 viewUICollectionViewDelegate
is used for further customization of your tableview, i.e. customizing the appearance and handling events.Since your Profile
class inherits from UICollectionViewController
you 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