Actually, there is a way to achieve that (I would not say that it is "elegant" and I would note that you should not do it most of the time) which is using the tag
property of the view:
An integer that you can use to identify view objects in your
application.
Consider that you have the following cell:

What you could do is to select the image view/ label and from the interface build, go to the attribute inspector and set its tag:

Therefore, without the need of creating a UICollectionViewCell
custom class what you should do in the cellForItemAt
is to cast the views based on their tag values:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let imageView = cell.viewWithTag(101) as! UIImageView
let label = cell.viewWithTag(102) as! UILabel
// ...
}
Obviously, at this point keep in mind that the given tag value for viewWithTag
must match the value set from the interface builder, as well as the sub casting, otherwise it will crash.