Can't dismiss mail view controller - swift

This code works and presents email form all filled out and sends the email just fine, just can't get it to dismiss after sending or trying to cancel.
I am sure I am missing something simple.
I also realize I have nothing in place if there is an error but at this point it works and sends email but i cant get it to dismiss
tried several suggestions I found online and there are other ways to do this but I am trying to understand why this is not working so want this way (or close) to work.... xcode 10.2.1
import UIKit
import MessageUI
class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func sendEmail(_ sender: Any) {
sendEmail()
}
func sendEmail() {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(["xxx#xx.com"])
mail.setSubject("test ")
mail.setPreferredSendingEmailAddress("xxx#jxxx.com")
present(mail, animated: true)
} else {
//show failure alert
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
}
compiles and sends email fine just cant dismiss the controller

All delegate methods should be in class scope , in your current code didFinishWith which should dimiss the mail is nested inside sendEmail
import UIKit
import MessageUI
class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func sendEmail(_ sender: Any) {
sendEmail()
}
func sendEmail() {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(["xxx#xx.com"])
mail.setSubject("test ")
mail.setPreferredSendingEmailAddress("xxx#jxxx.com")
present(mail, animated: true)
} else {
//show failure alert
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
}

Related

do something after first view controller appears

I have a View Controller with a button..
This button calls a "Present as Popover Seague" to a second view controller.
The second view controller has a close button with this function:
#IBAction func exit(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
Now I would like to do something in the first controller, after the second Controller is dismissed.
In the first view controller I tried this functions:
override func viewDidAppear(_ animated: Bool) {
print("viewDidAppear")
}
override func viewWillAppear(_ animated: Bool) {
print("viewWillAppear")
}
but no console log will shown.
Where is my mistake?
FirstViewContorller
import UIKit
class firstVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//ERROR
secondVC.dismissCompletion = {
print("dismissCompletion")
}
}
}
SecondVC (popover)
import UIKit
class secondVC: UIViewController {
var dismissCompletion: (() -> Void)?
// EXIT POPOVER
#IBAction func exit(_ sender: UIButton) {
self.dismiss(animated: true, completion: dismissCompletion)
}
}
The viewDidAppear() method of the main view controller won't be called because of the popover presentation style you use. If you choose to present the second view controller full screen - those methods will fire.
If we're sticking with the popover, the first thing you need to do is in your second view controller, the one that's being presented, add a property for a closure that will be executed upon its dismiss:
class PopoverViewController: UIViewContoller {
var dismissCompletion: (() -> Void)?
#IBAction func exit(_ sender: UIButton) {
self.dismiss(animated: true, completion: dismissCompletion)
}
}
And in your main view controller you define what needs to be done upon the popover's dismiss:
popoverViewContoller.dismissCompletion = {
// do stuff
}
UPDATE:
I assume you've setup the segue in your storyboard. I also assume that in your storyboard you've given the view controllers their respective class names:
This is what your code should look like:
class FirstViewController: UIViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let secondVC = segue.destination as? SecondViewController else {
return
}
secondVC.dismissCompletion = {
print("Popover dismissed")
}
}
}
class SecondViewController: UIViewController {
var dismissCompletion: (() -> Void)?
#IBAction func exit(_ sender: Any) {
dismiss(animated: true, completion: dismissCompletion)
}
}
Please note the classes naming and the way I got the secondVC instance.

MFMessageComposer cancel button not visible in iOS 11

The cancel button on right top corner after a message composer has been presented is not visible in iOS 11 devices. As shown in the screenshot, cancel button works but is not visible. Once we press on it, the screen dismisses.
I have tried like this:
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func Messages(_ sender: UIButton) {
if MFMessageComposeViewController.canSendText() == true {
let recipients:[String] = ["1500"]
let messageController = MFMessageComposeViewController()
messageController.messageComposeDelegate = self
messageController.recipients = recipients
messageController.body = "Your_text"
self.present(messageController, animated: true, completion: nil)
} else {
//handle text messaging not available
}
}
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
controller.dismiss(animated: true, completion: nil)
}
}
Added Messages and MessageUI frameworks and imported in view controller.
Screenshot:
Image:

A better way to get show a view controller after tapping selection from Google Places

I'm following a tutorial to use Google Places into my app and I noticed that after clicking on a filtered result the Google Places View Controller gets dismissed and shows the previous screen that called Google Places. I am not sure how to tweak it so that after the user taps on a result it goes straight to another controller. Right now I have it where after the results is tapped, the Google Places controller is dismissed and I call a present to bring up the other controller. How do I go to the next controller after the user taps on the result without going back to the controller that called Google Places?
import UIKit
import GooglePlaces
class TravelerViewController: UIViewController {
#IBAction func autocompleteClicked(_ sender: UIButton) {
let autocompleteController = GMSAutocompleteViewController()
autocompleteController.delegate = self
present(autocompleteController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
extension TravelerViewController: GMSAutocompleteViewControllerDelegate {
// Handle the user's selection.
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
print("Place name: \(place.name)")
print("Place address: \(place.formattedAddress)")
print("Place attributions: \(place.attributions)")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "TravelerResults")
dismiss(animated: true, completion: nil)
self.present(controller, animated: true, completion: nil)
}
func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
// TODO: handle the error.
print("Error: ", error.localizedDescription)
}
// User canceled the operation.
func wasCancelled(_ viewController: GMSAutocompleteViewController) {
dismiss(animated: true, completion: nil)
}
// Turn the network activity indicator on and off again.
func didRequestAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
func didUpdateAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
Make separate function for open View Controller.
#objc func openVC() {
let controller = self.storyboard?.instantiateViewController(withIdentifier: "TravelerResults")
self.present(controller!, animated: true, completion: nil)
}
Call this function in AutoComplete.
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
print("Place name: \(place.name)")
print("Place address: \(place.formattedAddress)")
print("Place attributions: \(place.attributions)")
dismiss(animated: true, completion: nil)
openVC()
}

How to use callback to send back data to previous ViewController

I have 2 ViewControllers like this:
I want when I press button in ViewController will present to ViewController2. Then when I input the text in TextField and press Back Button in ViewController2, will send that text to button in ViewController1.
I can solve this with delegate or using reference the class ViewController in ViewController2. (You can see with my comment code)
But now I want to use CallBack to send back data.
How to do this:
Here is ViewController:
import UIKit
class ViewController: UIViewController {
#IBOutlet var button: UIButton!
var myString = ""
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
button.setTitle(myString, forState: .Normal)
}
#IBAction func goToViewController2(sender: AnyObject) {
let vc2 = storyboard?.instantiateViewControllerWithIdentifier("ViewController2") as! ViewController2
// vc2.vc1 = self
presentViewController(vc2, animated: true, completion: nil)
}
}
And ViewController2:
import UIKit
class ViewController2: UIViewController, UITextFieldDelegate {
#IBOutlet var textField: UITextField!
// var vc1: ViewController?
var callback: ((String) -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
}
func textFieldDidEndEditing(textField: UITextField) {
callback!(textField.text!)
}
#IBAction func backToViewController(sender: AnyObject) {
// vc1?.myString = textField.text!
dismissViewControllerAnimated(true, completion: nil)
}
}
And I don't want to use with completion block in dismiss function, I just want to use my own callback method.
In vc1, set vc2's callback property before presenting it:
#IBAction func goToViewController2(sender: AnyObject) {
let vc2 = storyboard?.instantiateViewControllerWithIdentifier("ViewController2") as! ViewController2
vc2.callback = { <Put your callback code here...> }
presentViewController(vc2, animated: true, completion: nil)
}
And then, in vc2 call the callback function before dismissing the view controller:
#IBAction func backToViewController(sender: AnyObject) {
callback?(textfield.text!)
dismissViewControllerAnimated(true, completion: nil)
}

How to implement the google sign-in in a UIButton rather than a UIView using google sign-in sdk?

Friends i am a ios swift learner at the post beginner level. I have been trying to do a google sign-in in my try-up app. Based on the resources from google developers and other tutorials i am successfully able to do the sign in using the UIView . Then i was trying to implement the same code in the action block of a UIButton which does not work for me. I want to place a custom image in the UIButton with no text. I tried many search engines which does not provide a clear answer. Please someone guide me
#IBOutlet weak var signInButton: GIDSignInButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().clientID = self.myClientId
GIDSignIn.sharedInstance().scopes.append("https://www.googleapis.com/auth/plus.login")
GIDSignIn.sharedInstance().scopes.append("https://www.googleapis.com/auth/plus.me")
GIDSignIn.sharedInstance().signInSilently()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
`
Here signInButton outlet is a UIView.
Please Tell me how to implement it into a UIButton's Touch-up inside action block or just a way to change the default icon that the google sdk returns.
Note: I am using Google-Sign-In SDK not Google-Plus-Sign-In SDK as it is Deprecated by google. Also no errors in the bridging headers , other Linker flags , URL Schema and GoogleSignIn Framework integration.
Please someone guide me. Thanks in advance.
Create your button on storyboard or programatically and on button action just do this.
#IBAction func googleSignInClicked(sender: UIButton) {
GIDSignIn.sharedInstance().signIn()
}
Implement the delegate function:
func signIn(signIn: GIDSignIn!, didSignInForUser user: GIDGoogleUser!, withError error: NSError!) {
}
to get the response.
Here i use code like:
In AppDelegate.swift
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
// Initialize sign-in
var configureError: NSError?
GGLContext.sharedInstance().configureWithError(&configureError)
assert(configureError == nil, "Error configuring Google services: \(configureError)")
GIDSignIn.sharedInstance().delegate = self
return true
}
func application(application: UIApplication,
openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
return GIDSignIn.sharedInstance().handleURL(url,
sourceApplication: sourceApplication,
annotation: annotation)
}
func signIn(signIn: GIDSignIn!, didSignInForUser user: GIDGoogleUser!,
withError error: NSError!) {
if (error == nil) {
// Perform any operations on signed in user here.
} else {
print("\(error.localizedDescription)")
}
}
func signIn(signIn: GIDSignIn!, didDisconnectWithUser user:GIDGoogleUser!,
withError error: NSError!) {
}
In LoginViewController.swift
#IBAction func btnGoogleLoginPressed(sender: AnyObject) {
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().signIn()
GIDSignIn.sharedInstance().signOut()
GIDSignIn.sharedInstance().disconnect()
GIDSignIn.sharedInstance().signIn()
}
func signInWillDispatch(signIn: GIDSignIn!, error: NSError!) {
//myActivityIndicator.stopAnimating()
}
// Present a view that prompts the user to sign in with Google
func signIn(signIn: GIDSignIn!,
presentViewController viewController: UIViewController!) {
self.presentViewController(viewController, animated: true, completion: nil)
//print("Sign in presented")
}
// Dismiss the "Sign in with Google" view
func signIn(signIn: GIDSignIn!,
dismissViewController viewController: UIViewController!) {
self.dismissViewControllerAnimated(true, completion: nil)
// print("Sign in dismissed")
}
func signIn(signIn: GIDSignIn!, didSignInForUser user: GIDGoogleUser!, withError error: NSError!){
let userId = user.userID // For client-side use only!
let idToken = user.authentication.idToken // Safe to send to the server
let fullName = user.profile.name
let givenName = user.profile.givenName
let familyName = user.profile.familyName
let email = user.profile.email
print("Welcome: ,\(userId), \(idToken), \(fullName), \(givenName), \(familyName), \(email)")
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let chooseCVC = mainStoryboard.instantiateViewControllerWithIdentifier("ChooseCategeoriesVC") as! ChooseCategeoriesVC
//chooseCVC.facebookUserDetailsDict = self.facebookUserDict
chooseCVC.loginTypeString = "Google"
// let rootViewController = self.window!.rootViewController as! UINavigationController
// rootViewController.pushViewController(chooseCVC, animated: true)
self.navigationController?.pushViewController(chooseCVC, animated: true)
}
// Finished disconnecting |user| from the app successfully if |error| is |nil|.
func signIn(signIn: GIDSignIn!, didDisconnectWithUser user: GIDGoogleUser!, withError error: NSError!){
}
Add GIDSignInDelegate in your UIViewController, and in ViewDidLoad(): GIDSignIn.sharedInstance().delegate = self
on button action, do this:
#IBAction func btnGoogleLoginOnClick(_ sender: UIButton)
{
GIDSignIn.sharedInstance().signIn()
}
And implement this delegate function:
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let error = error {
print(error.localizedDescription)
return
}
let authentication = user.authentication
print("Access token:", authentication?.accessToken!)
}
func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!, withError error: Error!) {
}
Add UIButton to storyboard with your image.
#IBAction func googleSignIn(sender: UIButton) {
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().signIn()
}
Delegate implementation will be like this -
// Present a view that prompts the user to sign in with Google
func signIn(signIn: GIDSignIn!,
presentViewController viewController: UIViewController!) {
self.presentViewController(viewController, animated: true, completion: nil)
print("Sign in presented")
}
// Dismiss the "Sign in with Google" view
func signIn(signIn: GIDSignIn!,
dismissViewController viewController: UIViewController!) {
self.dismissViewControllerAnimated(true, completion: nil)
print("Sign in dismissed")
}
Don't forget to declare delegate 'GIDSignInUIDelegate' in your UIViewController implementation
In swift 4 and 5 simply do that
1-Appdelegate add these two lines but before configure download the infoplist it will generate when you will create OAuth Id below mentioned GSignID then write these two lines otherwise your app will crash after two or three try
GIDSignIn.sharedInstance().clientID = GSignID
GIDSignIn.sharedInstance().delegate = self
2-Go to your viewcontroller in which you want to implement google sign in import GIDSignInDelegate
extension RegisterMenuVC : GIDSignInDelegate{
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let error = error {
if (error as NSError).code == GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
print("The user has not signed in before or they have since signed out.")
} else {
print("\(error.localizedDescription)")
}
return
}
print("User details")
print(user.profile.name)
print(user.profile.familyName)
print(user.profile.imageURL(withDimension: 200))
let creds = user.authentication.idToken
}
}
3- Simply call the action in button
GIDSignIn.sharedInstance()?.signIn()