1
\$\begingroup\$

Within my app i have a UIButton (avatar button which shows a profileVC) within a UICollectionView this button shows up in about 4-5 other views. I'm currently adding the target within cellForItemAtIndexPath in each view controller and pushing the view from a public function pushNewViews. I was wondering if they was a better way to do this? (less repetition)

    class ShotsViewController: UIViewController{
    //CollectionView 
        override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell2", forIndexPath: indexPath) as!  mainCell
            cell.userImg.addTarget(self, action: #selector(ShotsViewController.showProfileView(_:)), forControlEvents: .TouchUpInside)
    }
//Target Func
    func showProfileView(sender: AnimatableButton) {
    let profileUser = shots[sender.tag].user
    pushNewViews.showProfileViewController(profileUser, navigation: navigationController!, storyboard: storyboard!)
}  

}

class pushNewViews{
    class func showProfileViewController(user: User, navigation: UINavigationController, storyboard: UIStoryboard){
        let vc = storyboard.instantiateViewControllerWithIdentifier("profileView") as! ProfileViewController
        vc.user = user
        navigation.pushViewController(vc, animated: true)
    }
}
\$\endgroup\$
1
\$\begingroup\$

You have a couple of options here depending on how explicitly you want to couple this view to the "push" action.

One option which I like is to rely on the existing responder chain. This gives you a lot of flexibility to allow views to emit events from anywhere in the view hierarchy and handle those events in a parent view, view controller, or the app delegate. I like that it avoids the overhead of connecting cells to view controllers (either through target action patterns as you have now or through a delegate protocol) but with the tradeoff that it becomes less obvious which class will respond to an event and requires more integration testing.

// Traverse the responder chain looking for the first responder who conforms to a provided generic type
extension UIResponder {
    func handoffEvent<T>(@noescape eventHandler: (T) -> Void) {
        var nextResponder: UIResponder?
        nextResponder = self
        while((nextResponder) != nil) {
            if let responder = nextResponder as? T {
                eventHandler(responder)
                return
            }
            nextResponder = nextResponder?.nextResponder()
        }
        // FIXME: you probably want some warning if you fail to find a responder of the expected type 
    }
}

// Define a protocol for our responder
protocol ControllerRouter {
    func pushUserProfileViewController(user: User)
}

// Views can then send events up the responder chain to be handled by some responder who adopts our protocol but the view doesn't need to know anything about who that responder is
class SomeCustomView: UIView {
    @IBAction func didPressProfileButton() {
        self.handoffEvent { (handler: ControllerRouter) in
            handler.pushCurrentUserProfileViewController(self.user)
        }
    }
}

// Some class in the responder chain needs to implement our protocol to handle the event
class ViewControllerOrAppDelegateOrWhatever: ControllerRouter {
    func pushCurrentUserProfileViewController(user: User) {
        guard
            let navigationController = self.navigationController,
            let profileViewController = self.storyboard.instantiateViewControllerWithIdentifier("profileView") as? ProfileViewController
        else {
            // FIXME: do not ignore unexpected nils
            return
        }
        profileViewController.user = user
        navigationController.pushViewController(profileViewController, animated: true)
    }
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks for the answer! I have to read more about UIResponders before implementing this. I'll let you know how to goes when i do. \$\endgroup\$ – kye Mar 26 '16 at 19:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.