0

I have a headerView in which I am having a float button. Since all the subitems in the float button are programmatically generated and it is a subview of headerView I am not able to call a performSegue. I tried creating a delegate method, But I totally messed it up. I know that protocols might be the fix, But i am quite unsure about how to exactly work it out. I am attaching the image of the headerView below

headerView code :

class ProfileHeader: UICollectionReusableView, FloatyDelegate {

@IBOutlet weak var pname: UILabel!
@IBOutlet weak var pusername: UILabel!
@IBOutlet weak var profilePic: UIImageView!
@IBOutlet weak var userDesc: UILabel!
@IBOutlet weak var allviews: UILabel!
@IBOutlet weak var allphotos: UILabel!



var user : User!
var userposts = [String]()
var currentViewUser: String!
var floaty = Floaty()

let uid = KeychainWrapper.standard.string(forKey: KEY_UID)

override func awakeFromNib() {
    super.awakeFromNib()

    user = User()

    fetchCurrentUser()
    profilePic.addBlackGradientLayer(frame: profilePic.bounds)
    layoutFAB()
}




func layoutFAB() {

    floaty.openAnimationType = .slideDown
    floaty.hasShadow = false
    floaty.addItem("Edit Profile", icon: UIImage(named: "")) { item in
        // here is where the segue is to be performed.
    }

    floaty.paddingY = (frame.height - 50) - floaty.frame.height/2
    floaty.fabDelegate = self
    floaty.buttonColor = UIColor.white
    floaty.hasShadow = true
    floaty.size = 45

    addSubview(floaty)

}


func fetchCurrentUser(){
    if uid != nil {
        FBDataservice.ds.REF_CURR_USER.observeSingleEvent(of: .value, with: { (snapshot) in
            if let allData = snapshot.value as? Dictionary<String, Any> {
                if let cred = allData["credentials"] as? Dictionary<String, Any> {
                    let user = User(userid: snapshot.key, userData: cred)
                    self.pusername.text = user.username
                    self.pname.text = user.name
                    self.userDesc.text = user.aboutme
                    self.allphotos.text = String(user.photos)
                    self.allviews.text = String(user.views)
                    if user.userPic != nil {
                        let request = URL(string: user.userPic)
                        Nuke.loadImage(with: request!, into: self.profilePic)
                    } else {
                        return
                    }
                }
            }

        })
    }
}
}

CollectionViewController code:

class ProfileVC: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {


@IBOutlet weak var collectionView: UICollectionView!

var user : User!
var userposts = [String]()
var post = [Posts]()
var currentViewUser: String!
var imagePicker: UIImagePickerController!
var fetcher: Fetcher!
var imageFromImagePicker = UIImage()


let uid = KeychainWrapper.standard.string(forKey: KEY_UID)

override func viewDidLoad() {
    super.viewDidLoad()

    user = User()
    fetcher = Fetcher()
    imagePicker = UIImagePickerController()
    imagePicker.delegate = self

    collectionView.delegate = self
    collectionView.dataSource = self



    initializeUserPost()



    let nib = UINib(nibName: "ProfileCell", bundle: nil)
    collectionView.register(nib, forCellWithReuseIdentifier: "ProfileCell")





}


func editProfileTapped() {
    performSegue(withIdentifier: "manageconnections", sender: nil)
}

@IBAction func manageConnections(_ sender: Any) {
    performSegue(withIdentifier: "manageconnections", sender: nil)
}


override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(true)
    collectionView.reloadData()
}


@IBAction func gotoSettings(_ sender: Any) {
    performSegue(withIdentifier: "openSettings", sender: nil)
}



@IBAction func newPost(_ sender: Any) {
    uploadNewPost()
}

@IBAction func backToFeed(_ sender: Any) {
    performSegue(withIdentifier: "feedroute", sender: nil)
}

@IBAction func searchUsers(_ sender: Any) {
    performSegue(withIdentifier: "searchNow", sender: nil)
}


func initializeUserPost() {

        FBDataservice.ds.REF_POSTS.observe(.value, with: { (snapshot) in
            if let snap = snapshot.value as? Dictionary<String, Any> {
                for snapy in snap {
                    if let userimages = snapy.value as? Dictionary<String, Any> {
                         let author = userimages["author"] as? String
                        if author! == self.uid! {
                            let images = userimages["imageurl"] as? String
                            self.userposts.append(images!)

                        }
                    }
                }
            }
            self.collectionView.reloadData()
        })


}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let userImages = userposts[indexPath.row]
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ProfileCell", for: indexPath) as! ProfileCell
    cell.fillCells(uid: uid!, userPost: userImages)
    return cell
}


func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}


func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return userposts.count


}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {


        let selecteditem : String!
        selecteditem = userposts[indexPath.row]
        performSegue(withIdentifier: "lol", sender: selecteditem)

}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "lol" {
        if let detailvc = segue.destination as? PhotoDetailVC {
            if let bro = sender as? String {
                detailvc.image = bro
            }
        }
    }
}


func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {

     let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "ProfileHeader", for: indexPath) as! ProfileHeader
    return view
}




func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        let width = (collectionView.bounds.size.width/3) - 1
        print(width)
        let size = CGSize(width: width, height: width)
        return size

}

func collectionView(_ collectionView: UICollectionView,
                    layout collectionViewLayout: UICollectionViewLayout,
                    minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return 1.0
}

func collectionView(_ collectionView: UICollectionView, layout
    collectionViewLayout: UICollectionViewLayout,
                    minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 1.0
}
Aakash Dave
  • 866
  • 1
  • 15
  • 30

1 Answers1

3

To solve your problem, you should use a pattern called delegate.

Delegate means that you need something to do the work of perform a segue for your HeaderView instead, something that can do it, in this case your ProfileVC.

1 - Create a protocol inside your HeaderView or another file, it's your call.

2 - Add a protocol variable type in your HeaderView as well and pass it from your viewController.

class ProfileHeader: UICollectionReusableView, FloatyDelegate {

@IBOutlet weak var pname: UILabel!
@IBOutlet weak var pusername: UILabel!
@IBOutlet weak var profilePic: UIImageView!
@IBOutlet weak var userDesc: UILabel!
@IBOutlet weak var allviews: UILabel!
@IBOutlet weak var allphotos: UILabel!

var user : User!
var userposts = [String]()
var currentViewUser: String!
var floaty = Floaty()

var delegate: HeaderViewDelegate!

let uid = KeychainWrapper.standard.string(forKey: KEY_UID)

override func awakeFromNib() {
    super.awakeFromNib()

    user = User()
    fetchCurrentUser()
    profilePic.addBlackGradientLayer(frame: profilePic.bounds)
    layoutFAB()
}

func setDelegate(delegate: HeaderViewDelegate) {
    self.delegate = delegate
}

func layoutFAB() {

    floaty.openAnimationType = .slideDown
    floaty.hasShadow = false
    floaty.addItem("Edit Profile", icon: UIImage(named: "")) { item in

         delegate.fabItemClicked()
    }

    floaty.paddingY = (frame.height - 50) - floaty.frame.height/2
    floaty.fabDelegate = self
    floaty.buttonColor = UIColor.white
    floaty.hasShadow = true
    floaty.size = 45

    addSubview(floaty)

}

func fetchCurrentUser(){
    if uid != nil {
        FBDataservice.ds.REF_CURR_USER.observeSingleEvent(of: .value, with: { (snapshot) in
            if let allData = snapshot.value as? Dictionary<String, Any> {
                if let cred = allData["credentials"] as? Dictionary<String, Any> {
                    let user = User(userid: snapshot.key, userData: cred)
                    self.pusername.text = user.username
                    self.pname.text = user.name
                    self.userDesc.text = user.aboutme
                    self.allphotos.text = String(user.photos)
                    self.allviews.text = String(user.views)
                    if user.userPic != nil {
                        let request = URL(string: user.userPic)
                        Nuke.loadImage(with: request!, into: self.profilePic)
                    } else {
                        return
                    }
                }
            }

        })
    }
}

public protocol HeaderViewDelegate {
     func fabItemClicked()
}

}

3 - Finally, edit your ProfileVC to implement the HeaderViewDelegate protocol and pass it to your HeaderView

class ProfileVC: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, HeaderViewDelegate {

@IBOutlet weak var collectionView: UICollectionView!

var user : User!
var userposts = [String]()
var post = [Posts]()
var currentViewUser: String!
var imagePicker: UIImagePickerController!
var fetcher: Fetcher!
var imageFromImagePicker = UIImage()

let uid = KeychainWrapper.standard.string(forKey: KEY_UID)

override func viewDidLoad() {
    super.viewDidLoad()

    user = User()
    fetcher = Fetcher()
    imagePicker = UIImagePickerController()
    imagePicker.delegate = self

    collectionView.delegate = self
    collectionView.dataSource = self

    initializeUserPost()


    let nib = UINib(nibName: "ProfileCell", bundle: nil)
    collectionView.register(nib, forCellWithReuseIdentifier: "ProfileCell")

}

//Method from the new protocol
func fabItemClicked(){
//Perform your segue here.
} 

func editProfileTapped() {
    performSegue(withIdentifier: "manageconnections", sender: nil)
}

@IBAction func manageConnections(_ sender: Any) {
    performSegue(withIdentifier: "manageconnections", sender: nil)
}


override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(true)
    collectionView.reloadData()
}


@IBAction func gotoSettings(_ sender: Any) {
    performSegue(withIdentifier: "openSettings", sender: nil)
}



@IBAction func newPost(_ sender: Any) {
    uploadNewPost()
}

@IBAction func backToFeed(_ sender: Any) {
    performSegue(withIdentifier: "feedroute", sender: nil)
}

@IBAction func searchUsers(_ sender: Any) {
    performSegue(withIdentifier: "searchNow", sender: nil)
}


func initializeUserPost() {

        FBDataservice.ds.REF_POSTS.observe(.value, with: { (snapshot) in
            if let snap = snapshot.value as? Dictionary<String, Any> {
                for snapy in snap {
                    if let userimages = snapy.value as? Dictionary<String, Any> {
                         let author = userimages["author"] as? String
                        if author! == self.uid! {
                            let images = userimages["imageurl"] as? String
                            self.userposts.append(images!)

                        }
                    }
                }
            }
            self.collectionView.reloadData()
        })


}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let userImages = userposts[indexPath.row]
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ProfileCell", for: indexPath) as! ProfileCell
    cell.fillCells(uid: uid!, userPost: userImages)
    return cell
}


func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}


func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return userposts.count


}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {


        let selecteditem : String!
        selecteditem = userposts[indexPath.row]
        performSegue(withIdentifier: "lol", sender: selecteditem)

}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "lol" {
        if let detailvc = segue.destination as? PhotoDetailVC {
            if let bro = sender as? String {
                detailvc.image = bro
            }
        }
    }
}


func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {

     let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "ProfileHeader", for: indexPath) as! ProfileHeader
     view.delegate = self
    return view
}




func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        let width = (collectionView.bounds.size.width/3) - 1
        print(width)
        let size = CGSize(width: width, height: width)
        return size

}

func collectionView(_ collectionView: UICollectionView,
                    layout collectionViewLayout: UICollectionViewLayout,
                    minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return 1.0
}

func collectionView(_ collectionView: UICollectionView, layout
    collectionViewLayout: UICollectionViewLayout,
                    minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 1.0
}

Don't forget to edit this method and pass self to your HeaderView:

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {

     let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "ProfileHeader", for: indexPath) as! ProfileHeader
     view.delegate = self
    return view
}
Luiz Fernando Salvaterra
  • 4,192
  • 2
  • 24
  • 42
  • Many thanks for the answer! I tried implementing this and it shows me the error `unexpectedly found nil while unwrapping an Optional value`. – Aakash Dave Oct 18 '17 at 11:52
  • probably your delegate is nill, try to use the func setDelegate() method – Luiz Fernando Salvaterra Oct 18 '17 at 11:53
  • Yeah, I tried that. Yet it shows me the same error on `self.delegate.fabItemClicked()`. Do I need to set the `delegate=self` in ProfileVC after adding the header class. – Aakash Dave Oct 18 '17 at 11:58
  • yes, to see if the delegate is a valid object, put a break point in your `viewForSupplementaryElementOfKind` method and see if the `view.delegate = self` is working. – Luiz Fernando Salvaterra Oct 18 '17 at 12:02
  • Woah! it Works. I added the delegate to viewDidLoad(). I am sorry, Delegates are not quite handy to me. I'll get the hang though. Well, Its working awesome now. Cheers mate – Aakash Dave Oct 18 '17 at 12:05