UIAlertController in tableViewCell - Swift - ios

I have a button in each cell in a tableviewcell. When the button is clicked, in the cell coding page I had an action which was working already. However I decided to let users confirm it before the action starts, so added an UIAlertController inside the action. The alert controller works fine in a UIviewcontroller, but in a UItableviewcell with the code below I got the error message: "does not have a member named presentviewcontroller". How can I make it work? My code is here. Thanks
#IBAction func followBtn_click(sender: AnyObject) {
if title == "Following" {
var refreshAlert = UIAlertController(title: "Sure?", message: "Do you want to unfollow this person?", preferredStyle: UIAlertControllerStyle.Alert)
refreshAlert.addAction(UIAlertAction(title: "Yes", style: .Default, handler: { (action: UIAlertAction!) in
var query = PFQuery(className: "follow")
query.whereKey("user", equalTo: PFUser.currentUser()!.username!)
query.whereKey("userToFollow", equalTo: usernameLbl.text!)
var objects = query.findObjects()
for object in objects! {
object.delete()
}
}))
refreshAlert.addAction(UIAlertAction(title: "No", style: .Default, handler: { (action: UIAlertAction!) in
println("Cancel unfollow")
}))
self.presentViewController(refreshAlert, animated: true, completion: nil)
/* self.window?.rootViewController?.presentViewController(refreshAlert, animated: true, completion: nil)
when I use the above to make it run, the code runs but nothing happens and I get the warning below in the output area
2015-07-02 08:57:22.978 MyLast[968:200872] Warning: Attempt to present <UIAlertController: 0x7f849c04ed20> on <UINavigationController: 0x7f8499c69020> whose view is not in the window hierarchy!
*/
}
else {
println(“rest will work”)
/* rest of the code */
}
}

Make sure your tableview controller is in hierarchy of navigation controller or embed in UINavigationController. Because UIAlertView is deprecated in swift And there is a UIAlertViewController and whenever you require or to present Alert you must have a Controller Hierarchy.
You also can show ActionSheet using same code by little changes UIAlertControllerStyle is action sheet.
One another suggestions is once try with remove your handler code to nil
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))

Related

Viewcontroller show another view controller as background layer when UIAlertViewController is shown?

I am facing a very strange issue. When a UIAlertController is shown then in background layer of the current view controller Previous UIAlertController is shown. I can't post screenshot of that screen.
Below is code for showing the UIAlertController
func showActionAlertView(alertData:[String],vc:UIViewController,singleType:Bool) -> Void {
let Alert = UIAlertController(title: alertData.first, message: NSLocalizedString(alertData[1], comment: ""), preferredStyle: UIAlertControllerStyle.alert)
Alert.addAction(UIAlertAction(title: alertData[2], style: .default, handler: { (action: UIAlertAction!) in
self.delegate?.okAction(controller: vc)
}))
if !singleType
{
Alert.addAction(UIAlertAction(title: alertData.last, style: .cancel, handler: { (action: UIAlertAction!) in
print("Handle Cancel Logic here")
self.delegate?.cancelAction(controller: vc)
}))
}
vc.present(Alert, animated: true, completion: nil)
}
Thanks
You must access the above function inside the same view controller where the value of vc also needs to be same.
class OneVC: UIViewController{
//Inside viewDidLoad
// value for vc should be given same as of present class name (OneVC)
showActionAlertView(alertData:[String], vc:OneVC, singleType:Bool) -> Void
}

UIAlertController Gets Deallocated Before Presenting It

I want to make a "rate app" alert, but for some reason it gets deallocated before showing it.
Here's the code:
func showAlert() {
if #available(iOS 8.0, *)
{
let alertController = UIAlertController(title: "Rate App", message: "Rate this app now", preferredStyle: .Alert)
let neverAction = UIAlertAction(title: "Never Show This Again", style: .Destructive, handler: { (action: UIAlertAction) in
self.userDefaults.setBool(true, forKey: "rateAlertRejected")
})
let rateAction = UIAlertAction(title: "Rate Now", style: .Default, handler: { (action: UIAlertAction) in
// Rate App
})
let remindAction = UIAlertAction(title: "Remind Me Later", style: .Cancel, handler: nil)
alertController.addAction(rateAction)
alertController.addAction(neverAction)
alertController.addAction(remindAction)
presentViewController(alertController, animated: true, completion: nil)
}
else
{
// Identical code (using UIAlertView) for iOS 7 which works perfectly
}
}
The method executes (in certain conditions, but for testing purposes it does it every time) after a custom unwind segue.
Why do I have this problem? I used UIAlertController before but I had no issues.
According to your comment you show showAlert in
a method that executes after a custom unwind segue.
unwind segue dismisses the view heriarchy and therefore your alert
does not get reference to a view controller to show from.
To solve this, show your alert in the View controller you unwind to or wait for the alert controller action to be completed before unwinding.

swift: view is not in the view hierarchy

I'm having a homeView which is the initial view to appear when app launches. In its viewWillAppear() method I check if the user is logged in. If not, he's redirected to login page and if he's not yet registered he'll be directed to sign-up page. I get an error when user clicks the sign-up button. The signUPView appears but it gives a warning that Attempt to present loginViewController on signupViewController whose view is not in the window hierarchy. I have show detail segues between sign-up and login views.
I'm performing the segue after uialert is presented.
I got solutions to put the code in viewDidAppear or ViewWillAppear method but I'm presenting the viewControllers upon button press. So I can't use these methods.
I have searched for a lot of solutions but unable to get the right one.
Please help!
Thank you.
segue triggering code from sign-up :
dispatch_async(dispatch_get_main_queue()) {
if self.registerSuccess == true {
let alert = UIAlertController(title: "Registered!", message: "You have registered successfully!", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: { (action) -> Void in
self.dismissViewControllerAnimated(true, completion: nil)
self.nameTextField.text = ""
self.emailTextField.text = ""
self.passwordTextField.text = ""
self.performSegueWithIdentifier("loginSegue", sender: self)
}))
self.presentViewController(alert, animated: true, completion: nil)
} else {
let alert = UIAlertController(title: "Registration Failed!", message: "Please try again", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: { (action) -> Void in
self.dismissViewControllerAnimated(true, completion: nil)
}))
self.presentViewController(alert, animated: true, completion: nil)
}
}
screenshot of my storyboard

NSNotification: Attempt to present UIAlertController on ViewController whose view is not in the window hierarchy

I'm trying to show a UIAlertController in my ViewController in a function that's been called via an NSNotification. However I'm getting the error:
Attempt to present <UIAlertController: 0x7fe013d05d40> on <submarine.ViewController: 0x7fe011f20370> whose view is not in the window hierarchy!
The NSNotification is posted from a completion block (callback I guess) from something else in my UI. Because it's a callback it's failing to display. Hence I thought I'd try NSNotificationCentre to get around the problem without using the rootViewController to display the alert.
My code is:
override func viewDidAppear(animated: Bool) {
// Handle onboarding
if needsOnboarding() {
handleOnboarding() // This create the completion block that posts the NSNotification
}
NSNotificationCenter.defaultCenter().addObserver(self, selector: "showTermsAlert:", name:"showTermsAlert", object: nil)
}
func showTermsAlert(notification: NSNotification) {
let termsAlert:UIAlertController = UIAlertController(title: "Terms And Conditions", message: "Please view the terms below before accepting them.", preferredStyle: UIAlertControllerStyle.Alert)
termsAlert.addAction(UIAlertAction(title: "View Terms", style: .Default, handler: { (action: UIAlertAction!) in
UIApplication.sharedApplication().openURL(NSURL(string: "my_terms_url")!)
}))
termsAlert.addAction(UIAlertAction(title: "I Agree to the Terms", style: .Default, handler: { (action: UIAlertAction!) in
self.onboardingFinished()
}))
self.presentViewController(termsAlert, animated: true, completion: nil)
}
Has anyone got an idea why this is happening? I don't see why it's not in the window hierarchy - it's being presented from the self viewController and is created in a top-level function inside the VC.
Thanks!
EDIT: original code inside the handleOnboarding():
Library used: Onboard
func handleOnboarding() {
let secondPage = OnboardingContentViewController(title: "What's going on?", body: "Submarine routes your data through our network, around any filters and restrictions, giving you unrestricted and unmonitored internet access.", image: UIImage(named: "back"), buttonText: "Next") { () -> Void in
// do something here when users press the button, like ask for location services permissions, register for push notifications, connect to social media, or finish the onboarding process
}
secondPage.movesToNextViewController = true
let thirdPage = OnboardingContentViewController(title: "Terms of Use", body: "You must agree to our Terms of Use to use Submarine.\nIf you don't, please close Submarine.", image: UIImage(named: "back"), buttonText: "View Terms") { () -> Void in
let termsAlert:UIAlertController = UIAlertController(title: "Terms And Conditions", message: "Please view the terms below before accepting them.", preferredStyle: UIAlertControllerStyle.Alert)
termsAlert.addAction(UIAlertAction(title: "View Terms", style: .Default, handler: { (action: UIAlertAction!) in
UIApplication.sharedApplication().openURL(NSURL(string: "my_policy_url")!)
}))
termsAlert.addAction(UIAlertAction(title: "I Agree to the Terms", style: .Default, handler: { (action: UIAlertAction!) in
self.onboardingFinished()
}))
self.presentViewController(termsAlert, animated: true, completion: nil)
// NSNotificationCenter.defaultCenter().postNotificationName("showTermsAlert", object: nil)
}
// Image
let onboardingVC = OnboardingViewController(backgroundImage: UIImage(named: "back"), contents: [secondPage, thirdPage])
self.navigationController?.presentViewController(onboardingVC, animated: false, completion: nil)
}
This happen when the presenting view controller is no longer part of the controller hierarchy, and it's view is no longer in the view hierarchy of any window. Most likely, the controller was dismissed or popped, but it heard the notification and attempted to present the alert controller.
You should manage your controller states more carefully. Perhaps remove observer when the controller is dismissed or popped from your controller hierarchy.
There are a few things i'd change in your code. Add a call to super in viewDidAppear:, and stop using the NSNotifications for your presentation. You don't know what thread showTermsAlert will get called on with this pattern. You can make your intent more explicit by calling showTermsAlert directly, and this will also guarantee you're on the main thread.
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// Handle onboarding
if needsOnboarding() {
self.showTermsAlert()
}
}
func showTermsAlert() {
let termsAlert:UIAlertController = UIAlertController(title: "Terms And Conditions", message: "Please view the terms below before accepting them.", preferredStyle: UIAlertControllerStyle.Alert)
termsAlert.addAction(UIAlertAction(title: "View Terms", style: .Default, handler: { (action: UIAlertAction!) in
UIApplication.sharedApplication().openURL(NSURL(string: "my_terms_url")!)
}))
termsAlert.addAction(UIAlertAction(title: "I Agree to the Terms", style: .Default, handler: { (action: UIAlertAction!) in
self.onboardingFinished()
}))
self.presentViewController(termsAlert, animated: true, completion: nil)
}

presentViewController isn't modal in shouldPerformSegueWithIdentifier

I'm trying to make an unwind segue depend on the user approving it in an alert dialog. However, when I run the code the dialog just quickly appears, disappears, and segues to the other view controller. I've tried making the alertDialog an instance variable of the class and that doesn't make a difference. The code below is inside a UIViewController subclass. What am I missing?
Thanks.
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
var shouldPerformSeque = true
if identifier == "startOver" {
let alertDialog = UIAlertController(title: "Warning",
message: "This will discard all data entered.",
preferredStyle: .Alert)
let okAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alertDialog.addAction(okAction)
let cancelActionHandler = {
(action: UIAlertAction!) -> Void in
shouldPerformSeque = false
}
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel,
handler: cancelActionHandler)
alertDialog.addAction(cancelAction)
presentViewController(alertDialog, animated: false, completion: nil)
}
return shouldPerformSeque
}
You can't do this in shouldPerformSegueWithIndentifier because the dialog will be presented asynchronously with regard to that method; by the time the action handler closure executes the method has already returned.
You need to display the dialog in response to whatever is triggering the segue and then perform the unwind programmatically depending on the user's response.

Resources