I am implementing a seemingly trivial and very popular use case where users select a contact, and send them a precomposed SMS.
However, the SMS ViewController dismisses itself automatically. This is easily reproducible.
How do I fix this?
Here's my code:
import UIKit
import MessageUI
import ContactsUI
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate, CNContactPickerDelegate{
let contactPickerViewController = CNContactPickerViewController()
let messageVC = MFMessageComposeViewController()
override func viewDidLoad() {
super.viewDidLoad()
contactPickerViewController.delegate = self
messageVC.messageComposeDelegate = self
}
func contactPicker(picker: CNContactPickerViewController, didSelectContact contact: CNContact) {
if let phoneNumberValue = contact.phoneNumbers.first?.value as? CNPhoneNumber {
if let phoneNumber = phoneNumberValue.valueForKey("digits") as? String {
// Configure message ViewController
messageVC.recipients = [phoneNumber]
messageVC.body = "Yoyoyo"
picker.presentViewController(messageVC, animated: true, completion: nil)
}
}
}
func messageComposeViewController(controller: MFMessageComposeViewController, didFinishWithResult result: MessageComposeResult) {
self.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func invite(sender: AnyObject) {
self.presentViewController(self.contactPickerViewController, animated: true, completion: nil)
}
}
The problem is that you are asking your picker to present the message view controller. When contactPicker:picker:didSelectContact: method is called, the picker view controller is automatically being dismissed by the system. This means that the view controller is going away and you are trying to use that view controller to present your next view controller.
What you need to do is have "ViewController" in this case present the message view controller. Below is an example of the portion of your code i changed. You'll notice i have a timer there. This is because if you try to present the messageVC right away, nothing will happen because the contacts view controller isn't done dismissing itself yet.
func contactPicker(picker: CNContactPickerViewController, didSelectContact contact: CNContact) {
if let phoneNumberValue = contact.phoneNumbers.first?.value as? CNPhoneNumber {
if let phoneNumber = phoneNumberValue.valueForKey("digits") as? String {
// Configure message ViewController
messageVC.recipients = [phoneNumber]
messageVC.body = "Yoyoyo"
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.presentViewController(self.messageVC, animated: true, completion: nil)
})
}
}
}
Related
Hi want want to make a refer a friend through SMS I write the following code. If user is pick on that open the SMS with text but it don't cancel again user will unable go back to app.
if indexPath.item == 1
{
//SMS
if MFMessageComposeViewController.canSendText() {
let urlToShare = self.referalmodeldata[0].referralCodeOnly
controller1.body = "Hey I just gave an Awesome Assessment on App you can also try it. I scored , Try to beat my score \(String(describing: urlToShare))"
controller1.messageComposeDelegate = self as? MFMessageComposeViewControllerDelegate
self.present(controller1, animated: true, completion: nil)
}
}
You haven't implemented MFMessageComposeViewControllerDelegate correctly in your code.
First of all you need to confirm MFMessageComposeViewControllerDelegate with your UIViewController as shown below:
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {
Next thing is you need add it's delegate method as shown below:
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
self.dismiss(animated: true, completion: nil)
}
Then you need to replace
controller1.messageComposeDelegate = self as? MFMessageComposeViewControllerDelegate
with
controller1.messageComposeDelegate = self
Demo code will be:
import UIKit
import MessageUI
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {
#IBAction func sendText(_ sender: Any) {
if (MFMessageComposeViewController.canSendText()) {
let controller = MFMessageComposeViewController()
controller.body = "Message Body"
controller.messageComposeDelegate = self
self.present(controller, animated: true, completion: nil)
}
}
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
self.dismiss(animated: true, completion: nil)
}
}
I'm having issues with Custom alerts and sending actions back to VC from which alert was called.
I have two classes:
Factory
ConfirmationAllert
User journey I'm trying to achieve:
The user performs actions in the Factory class after he finishes I call ConfirmationAllert using such code:
func showAlert() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let myAlert = storyboard.instantiateViewController(withIdentifier: "ConfirmationAllert")
myAlert.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
myAlert.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
(view as? UIViewController)?.present(myAlert, animated: true, completion: nil)
}
In ConfirmationAllert class I have button, which:
dismisses alert
sends action to Factory - this action is to dismiss Factory VC and go back to previous VC.
First action completes successfully, the second action not working. I'm using protocols to send the second action to Factory VC, but something is not working, and I don't know what.
Here is my code:
Factory
final class FactoryViewController: UIViewController {
let alert = ConfirmationAllert()
#IBAction func didPressSave(_ sender: UIButton) {
showAlert()
}
func goToPreviousVc() {
alert.delegate = self
print("Inside factory") -> print don't get called
// navigationController?.popViewController(animated: true) -> none of this works
// dismiss(animated: true, completion: nil) -> none of this works
}
}
extension FactoryViewController: ConfirmationAllertDelegate {
func dismissVC() {
goToPreviousVc()
print("Go to previous")
}
}
ConfirmationAllert
protocol ConfirmationAllertDelegate {
func dismissVC()
}
class ConfirmationAllert: UIViewController {
var delegate: ConfirmationAllertDelegate?
#IBAction func didPressOk(_ sender: UIButton) {
self.delegate?.dismissVC()
}
}
I didn't include viewDidLoad methods as I'm not calling anything there.
My issue is that method goToPreviousVc() doesn't perform any actions.
Thank you in advance for your help!
I guess your problem is that you setup your ConfirmationAllertDelegate at goToPreviousVc that supposed to be called using that delegate.
Instead, try to set up you delegate when you creating myAlert object
let myAlert = storyboard.instantiateViewController(withIdentifier: "ConfirmationAllert")
(myAlert as? ConfirmationAllert).delegate = self
// the rest of your code
After that, your alert will have a delegate since it was created and when you press the button, it should work as you expect.
Try to use below code
func showAlert() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let myAlert = storyboard.instantiateViewController(withIdentifier: "ConfirmationAllert") as! ConfirmationAllert
myAlert.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
myAlert.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
myAlert.delegate = self
present(myAlert, animated: true, completion: nil)
}
Im trying to acheive the same results as: Sending SMS from Contacts Fails
Sending an SMS or "App Invite" to contact(s).
The solution to this problem works, but it only allows you to send an SMS to one person at a time. What I want to do is send an SMS to multiple contacts at one time. Forgive me if this is fairly easy. I've been up for the past 14 hours programming and most things aren't making sense to me right now.
Heres my code:
//MARK : VARIABLES
let contactPickerViewController = CNContactPickerViewController()
let messageViewController = MFMessageComposeViewController()
//MARK : VIEW DID LOAD
override func viewDidLoad() {
super.viewDidLoad()
//-- set delegates equal to self
contactPickerViewController.delegate = self
messageViewController.messageComposeDelegate = self
}
//MARK : MFMESSAGECOMPOSE & CNCONTACTPICKERDELEGATE
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
self.dismiss(animated: true, completion: nil)
}
func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
//-- select contacts and present message compose view controller
contacts.forEach { (contact) in
for data in contact.phoneNumbers {
let phoneNo = data.value
//-- configure message view controller
messageViewController.recipients = [phoneNo]
messageViewController.body = "Testing Testing"
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.present(self.messageViewController, animated: true, completion: nil)
})
}
}
}
func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
print("cancelled")
}
In your for loop you are attempting to display an MFMessageComposeViewController for each recipient. This won't work as it will attempt to present multiple view controllers concurrently.
You can present a single MFMessageComposeViewController that has all of the recipients specified:
var recipients = [String]()
contacts.forEach { (contact) in
for data in contact.phoneNumbers {
let phoneNo = data.value
recipients.append(phoneNo.stringValue)
}
}
//-- configure message view controller
messageViewController.recipients = recipients
messageViewController.body = "Testing Testing"
self.present(self.messageViewController, animated: true, completion: nil)
I'm trying to prompt the user to create a new contact and pass in information. (specifically a phone and email)
I've found numerous examples of using a CNMutableContact and adding an email to it. However, any of the code involving the CNContact gives me a "Use of undeclared type" error.
How can I setup my class to prompt the user to save the contact?
import ContactsUI
//add CNContactViewControllerDelegate to your ViewController
class ViewController: UIViewController , CNContactViewControllerDelegate {
func addPhoneNumber(phNo : String) {
if #available(iOS 9.0, *) {
let store = CNContactStore()
let contact = CNMutableContact()
let homePhone = CNLabeledValue(label: CNLabelHome, value: CNPhoneNumber(stringValue :phNo ))
contact.phoneNumbers = [homePhone]
let controller = CNContactViewController(forUnknownContact : contact)
controller.contactStore = store
controller.delegate = self
self.navigationController?.setNavigationBarHidden(false, animated: true)
self.navigationController!.pushViewController(controller, animated: true)
}
}
You Can Do Something Like This.
extension ViewController: CNContactViewControllerDelegate {
func showNewContactViewController() {
let contactViewController: CNContactViewController = CNContactViewController(forNewContact: nil)
contactViewController.contactStore = CNContactStore()
contactViewController.delegate = self
let navigationController: UINavigationController = UINavigationController(rootViewController: contactViewController)
present(navigationController, animated: false) {
print("Present")
}
}
}
Swift 4
import ContactsUI
implement delegate CNContactViewControllerDelegate
#IBAction func UserTap_Handler(_ sender: Any) {
self.navigationController?.isNavigationBarHidden = false
let con = CNContact()
let vc = CNContactViewController(forNewContact: con)
vc.delegate = self
_ = self.navigationController?.pushViewController(vc, animated: true)
}
//MARK:- contacts delegates
func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) {
print("dismiss contact")
self.navigationController?.popViewController(animated: true)
}
func contactViewController(_ viewController: CNContactViewController, shouldPerformDefaultActionFor property: CNContactProperty) -> Bool {
return true
}
I'm using Xcode 8 to build an application for iPhone.My simple app has an button. When user tap on this button, messageComposeViewController is called and with phone number and message content filled.The message went through successfully when I clicked on Send button.The problem is MessageComposeViewController is showing only once.After the message sent, when I tapped on the button to call it, a black screen showed up instead of the message composer.My code is attached below. I appreciate any help.
import UIKit
import MessageUI
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {
let msg = MFMessageComposeViewController()
#IBOutlet weak var coordinate_label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func sendMessage(_ sender: AnyObject) {
self.msg.body = "Message Content"
self.msg.recipients = ["xxx-xxx-xxxx"]
self.msg.messageComposeDelegate = self
self.present(msg, animated: false, completion: nil)
}
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
switch result.rawValue {
case MessageComposeResult.cancelled.rawValue:
dismiss(animated: true, completion: nil)
case MessageComposeResult.failed.rawValue:
dismiss(animated: true, completion: nil)
case MessageComposeResult.sent.rawValue:
dismiss(animated: true, completion: nil)
default:
break;
}
}
}
Try this code
import Foundation
import MessageUI
let textMessageRecipients = ["1-800-867-5309"] // for pre-populating the recipients list (optional, depending on your needs)
class MessageComposer: NSObject, MFMessageComposeViewControllerDelegate {
// A wrapper function to indicate whether or not a text message can be sent from the user's device
func canSendText() -> Bool {
return MFMessageComposeViewController.canSendText()
}
// Configures and returns a MFMessageComposeViewController instance
func configuredMessageComposeViewController() -> MFMessageComposeViewController {
let messageComposeVC = MFMessageComposeViewController()
messageComposeVC.messageComposeDelegate = self // Make sure to set this property to self, so that the controller can be dismissed!
messageComposeVC.recipients = textMessageRecipients
messageComposeVC.body = "Hey friend - Just sending a text message in-app using Swift!"
return messageComposeVC
}
// MFMessageComposeViewControllerDelegate callback - dismisses the view controller when the user is finished with it
func messageComposeViewController(controller: MFMessageComposeViewController!, didFinishWithResult result: MessageComposeResult) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
}
Note, latest syntax 2017 ..
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
controller.dismiss(animated: true, completion: nil)
}
In Your viewController ..
import UIKit
class ViewController: UIViewController {
// Create a MessageComposer
let messageComposer = MessageComposer()
#IBAction func sendTextMessageButtonTapped(sender: UIButton) {
// Make sure the device can send text messages
if (messageComposer.canSendText()) {
// Obtain a configured MFMessageComposeViewController
let messageComposeVC = messageComposer.configuredMessageComposeViewController()
// Present the configured MFMessageComposeViewController instance
// Note that the dismissal of the VC will be handled by the messageComposer instance,
// since it implements the appropriate delegate call-back
presentViewController(messageComposeVC, animated: true, completion: nil)
} else {
// Let the user know if his/her device isn't able to send text messages
let errorAlert = UIAlertView(title: "Cannot Send Text Message", message: "Your device is not able to send text messages.", delegate: self, cancelButtonTitle: "OK")
errorAlert.show()
}
}
}