用Swift寫一個(gè)發(fā)送郵件的iOS用戶反饋

為了接收用戶反饋,很多iOS應(yīng)用都會(huì)在設(shè)置頁(yè)面中,加入發(fā)送郵件功能——尤其當(dāng)應(yīng)用是由個(gè)人開發(fā)者開發(fā)時(shí)。當(dāng)然iOS中郵件的發(fā)送方式有很多種,有體驗(yàn)相對(duì)較差openURL跳轉(zhuǎn)方式,也有調(diào)用其他第三方庫(kù)等辦法。

不過(guò)較常用且方便的,還是如下圖(應(yīng)用為潮汐),調(diào)用系統(tǒng)的MFMailComposeViewController視圖在應(yīng)用內(nèi)完成郵件發(fā)送,并返回應(yīng)用。

chaoxi_feedback

下面就詳解下這種方式的實(shí)現(xiàn)步驟。

一、建立靜態(tài)列表

首先,拖一個(gè)Table View Controllermain.storyboard中,并選中Table View在右側(cè)屬性面板中將其設(shè)置為靜態(tài)列表Static Cells

static_cells

為了演示方便這里就先創(chuàng)建一個(gè)Section,其中有兩行Cell。兩個(gè)Cell的Style都設(shè)置為Basic,并將Title修改如下。

table_view_cel

下一步是建立這個(gè)Table ViewController。新建一個(gè)Cocoa Touch Class文件,并選擇Subclass of UITableViewController

table_view_cel

接著在右邊工具欄面板中為其設(shè)置好Custom Class。由于這里暫時(shí)用不到這個(gè)UITableViewController類里的內(nèi)容,可以把他們都注釋掉或刪掉。接著在其中重寫一個(gè)tableView點(diǎn)選的函數(shù):

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){

 if indexPath.section == 0 && indexPath.row == 0 {
  print("給應(yīng)用評(píng)分")
 }
  
 if indexPath.section == 0 && indexPath.row == 1 {
  print("意見反饋")
 }
} 

在模擬器中運(yùn)行,點(diǎn)按Cell,檢查output區(qū)中print的內(nèi)容是否正常,然后就可以進(jìn)入下一步。

二、MFMailComposeViewController

處理完UITableViewController以后,就可以開始調(diào)用郵件視圖了。不過(guò)先不急著寫代碼,首先需要導(dǎo)入框架MessageUI.framework。在項(xiàng)目設(shè)置Build Phases的Link Binary With Libraries中添加MessageUI.framework

messageui

然后在Controller里導(dǎo)入頭文件import MessageUI。并給Controller加上MFMailComposeViewControllerDelegate協(xié)議。

上述步驟搞定后,就可以愉快地寫代碼了。首先先寫個(gè)函數(shù),來(lái)配置發(fā)郵件的視窗。

func configuredMailComposeViewController() -> MFMailComposeViewController {
    
 let mailComposeVC = MFMailComposeViewController()
 mailComposeVC.mailComposeDelegate = self
        
 //設(shè)置郵件地址、主題及正文
 mailComposeVC.setToRecipients(["<你的郵箱地址>"])
 mailComposeVC.setSubject("<郵件主題>")
 mailComposeVC.setMessageBody("<郵件正文>", isHTML: false)
        
 return mailComposeVC
    
}

鑒于這種發(fā)送郵件的方式,要求用戶已經(jīng)在設(shè)備上至少添加有一個(gè)郵箱,所以對(duì)沒有設(shè)置郵箱的用戶,還應(yīng)予以提示。因此這里再寫一個(gè)函數(shù),來(lái)配置針對(duì)未設(shè)置郵箱用戶的彈窗提醒。

func showSendMailErrorAlert() {

 let sendMailErrorAlert = UIAlertController(title: "無(wú)法發(fā)送郵件", message: "您的設(shè)備尚未設(shè)置郵箱,請(qǐng)?jiān)凇班]件”應(yīng)用中設(shè)置后再嘗試發(fā)送。", preferredStyle: .Alert)
 sendMailErrorAlert.addAction(UIAlertAction(title: "確定", style: .Default) { _ in })
 self.presentViewController(sendMailErrorAlert, animated: true){}
 
}

搞定這倆函數(shù)后,就可以在之前的tableView函數(shù)中調(diào)用兩者了。

if indexPath.section == 0 && indexPath.row == 1 {
 print("意見反饋")
            
 if MFMailComposeViewController.canSendMail() {
  //注意這個(gè)實(shí)例要寫在if block里,否則無(wú)法發(fā)送郵件時(shí)會(huì)出現(xiàn)兩次提示彈窗(一次是系統(tǒng)的)
  let mailComposeViewController = configuredMailComposeViewController()
  self.presentViewController(mailComposeViewController, animated: true, completion: nil)
 } else {
  self.showSendMailErrorAlert()
 }
            
}

最后,寫上dismiss郵件視窗的函數(shù),就大功告成了。

func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
        
 switch result.rawValue {
 case MFMailComposeResultCancelled.rawValue:
  print("取消發(fā)送")
 case MFMailComposeResultSent.rawValue:
  print("發(fā)送成功")
 default:
  break
 }   
 self.dismissViewControllerAnimated(true, completion: nil)
        
}

三、加入設(shè)備及應(yīng)用信息

為了獲得更加準(zhǔn)確的反饋信息,可以在郵件正文里加入反饋者的設(shè)備及應(yīng)用信息。那怎樣使用swift獲得設(shè)備信息呢?可以如下通過(guò)UIDevice取得。

//獲取設(shè)備名稱
let deviceName = UIDevice.currentDevice().name
//獲取系統(tǒng)版本號(hào)
let systemVersion = UIDevice.currentDevice().systemVersion
//獲取設(shè)備的型號(hào)
let deviceModel = UIDevice.currentDevice().model
//獲取設(shè)備唯一標(biāo)識(shí)符
let deviceUUID = UIDevice.currentDevice().identifierForVendor?.UUIDString

這里的設(shè)備型號(hào)deviceModel只能獲知設(shè)備的簡(jiǎn)單區(qū)分(如是iPhone還是iPad),如果需要詳細(xì)的iOS設(shè)備信息,還需要寫一個(gè)UIDevice的擴(kuò)展。

public extension UIDevice {

    var modelName: String {
        var systemInfo = utsname()
        uname(&systemInfo)
        let machineMirror = Mirror(reflecting: systemInfo.machine)
        let identifier = machineMirror.children.reduce("") { identifier, element in
            guard let value = element.value as? Int8 where value != 0 else { return identifier }
            return identifier + String(UnicodeScalar(UInt8(value)))
        }

        switch identifier {
        case "iPod5,1":                                 return "iPod Touch 5"
        case "iPod7,1":                                 return "iPod Touch 6"
        case "iPhone3,1", "iPhone3,2", "iPhone3,3":     return "iPhone 4"
        case "iPhone4,1":                               return "iPhone 4s"
        case "iPhone5,1", "iPhone5,2":                  return "iPhone 5"
        case "iPhone5,3", "iPhone5,4":                  return "iPhone 5c"
        case "iPhone6,1", "iPhone6,2":                  return "iPhone 5s"
        case "iPhone7,2":                               return "iPhone 6"
        case "iPhone7,1":                               return "iPhone 6 Plus"
        case "iPhone8,1":                               return "iPhone 6s"
        case "iPhone8,2":                               return "iPhone 6s Plus"
        case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":return "iPad 2"
        case "iPad3,1", "iPad3,2", "iPad3,3":           return "iPad 3"
        case "iPad3,4", "iPad3,5", "iPad3,6":           return "iPad 4"
        case "iPad4,1", "iPad4,2", "iPad4,3":           return "iPad Air"
        case "iPad5,3", "iPad5,4":                      return "iPad Air 2"
        case "iPad2,5", "iPad2,6", "iPad2,7":           return "iPad Mini"
        case "iPad4,4", "iPad4,5", "iPad4,6":           return "iPad Mini 2"
        case "iPad4,7", "iPad4,8", "iPad4,9":           return "iPad Mini 3"
        case "iPad5,1", "iPad5,2":                      return "iPad Mini 4"
        case "iPad6,7", "iPad6,8":                      return "iPad Pro"
        case "AppleTV5,3":                              return "Apple TV"
        case "i386", "x86_64":                          return "Simulator"
        default:                                        return identifier
        }
    }

}

//調(diào)用
let modelName = UIDevice.currentDevice().modelName

獲取這些設(shè)備信息后,就可以在郵件正文中加入它們了,比如:

mailComposeVC.setMessageBody("\\\\n\\\\n\\\\n系統(tǒng)版本:\\\\(systemVersion)\\\\n設(shè)備型號(hào):\\\\(modelName)", isHTML: false)

同理,也可以獲得應(yīng)用的相關(guān)信息。

let infoDic = NSBundle.mainBundle().infoDictionary

// 獲取App的版本號(hào)
let appVersion = infoDic?["CFBundleShortVersionString"]
// 獲取App的build版本
let appBuildVersion = infoDic?["CFBundleVersion"]
// 獲取App的名稱
let appName = infoDic?["CFBundleDisplayName"]

到這里,一個(gè)調(diào)用MFMailComposeViewController的iOS郵件反饋就基本寫完了。運(yùn)行的時(shí)候,要注意用虛擬器的話可能會(huì)報(bào)錯(cuò),測(cè)試需要真機(jī)環(huán)境。效果如下。

demo

原文地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容