1

Say we have a custom control, that at some state should pop the option list for user to choose from (UIAlertController). Usually when we are in a view controller we use presentViewController method, but in this case we have no access to a parent UIViewController which holds this method. Although there's seems to be some way to get to UIViewController from subviews, it's considered against MVC design pattern. Then how would you do this? I want to keep view controller as lean as possible and custom view (uicontrol) self sufficient, so I wouldn't want to move this logic to view controller. Although all your advice and suggestions are welcome and appreciated.

mra214
  • 509
  • 1
  • 7
  • 18
  • 2
    Possible duplicate of [How to present UIAlertController when not in a view controller?](http://stackoverflow.com/questions/26554894/how-to-present-uialertcontroller-when-not-in-a-view-controller) – Vlad Apr 21 '16 at 08:10
  • 1
    one option would be delegation, u can call a delegate method from your custom view to `view controller` to present a view controller – Shankar BS Apr 21 '16 at 08:24

3 Answers3

6

You can always use application rootviewController to present your alert view controller :) You dont always have to opt for your view controller's view :) rootview controller is always accessible no matter where your control is :)

Here is what you can do :)

Objective C

 AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate.window.rootViewController presentViewController:your_alert_view_controller animated:YES completion:nil];

Swift

let appDelegate : AppDelegate? = UIApplication.sharedApplication().delegate as? AppDelegate
if let unwrappedAppdelegate = appDelegate {
     unwrappedAppdelegate.window!.rootViewController! .presentViewController(alert, animated: true, completion: nil)
}

Hope my answer helped you :)

Sandeep Bhandari
  • 19,999
  • 5
  • 45
  • 78
2

I would prefer delegation or target-action. It's much cleaner way because in this case you don't use an AppDelegate. Also it's not a subview's responsibility to show something. Example with delegation:

protocol MyViewDelegate: class {
    func somethingHappenedToMyView(view: MyView)
}

class MyView: UIView {
    weak var delegate: MyViewDelegate?

    func somethingHappened() {
        delegate?.somethingHappenedToMyView(self)
    }
}

class ViewController: UIViewController, MyViewDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        myView.delegate = self
    }

    func somethingHappenedToMyView(view: MyView) {
        presentViewController(someViewController, animated: true, completion: nil)
    }
}

Example with target-action:

class MyView: UIControl {
    func somethingHappened() {
        sendActionsForControlEvents(.ValueChanged) // .ValueChanged is an example. Maybe other events will be more meaningful for your situation
    }
}

class ViewController: UIViewController, MyViewDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        myView.addTarget(self, action: #selector(self.somethingHappenedToMyView(_:)), forControlEvents: .ValueChanged)
    }

    func somethingHappenedToMyView(view: MyView) {
        presentViewController(someViewController, animated: true, completion: nil)
    }
}
Alexander Doloz
  • 4,078
  • 1
  • 20
  • 36
  • Yes, I think using delegate indeed should be better approach. But this way MyCustomControl stops to be self-contained solution and I need to supply data to view controller to present it somehow. Then after user selects some value I need to return it to ui control and decide what to do next. – mra214 Apr 21 '16 at 10:42
1

Presenting from delegate root view controller will not work in many cases (nested presentation, tab's more controller etc).

Better find parent UIViewController responder by looping through view's responder chain. Use this macro:

#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})

In view:

UIViewController *parentViewController = UIViewParentController(self);

Related questions: one, two.

Community
  • 1
  • 1
Roman B.
  • 3,598
  • 1
  • 25
  • 21