I'm simply trying to get it so that when you press a button, you display the user's music library, and then you can select a song. I've found that the way to do this is through MPMediaPickerController but I've been struggling to get it to work. This is what my program looks like so far:
import MediaPlayer
import UIKit
class ViewController: UIViewController, MPMediaPickerControllerDelegate {
var mediaPicker: MPMediaPickerController?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func showSongs(_ sender: Any) {
displayMediaPicker()
}
func displayMediaPicker() {
mediaPicker = MPMediaPickerController(mediaTypes: .music)
if let picker = mediaPicker {
picker.delegate = self
picker.allowsPickingMultipleItems = false
picker.showsCloudItems = false
picker.prompt = "Please Pick a Song"
view.addSubview(picker.view)
}
}
}
I know that there should be a property [self presentViewController:picker animated: true completion:nil] after view.addSubview(picker.view), but when I type it I have the options presentingViewController & presentedViewContoller, but no presentViewController
Any help would be much appreciated
The function presentViewController: animated: completion: function was renamed in Swift 3 so now it looks like this:
present(viewControllerToPresent: UIViewController, animated: Bool, completion: (() -> Void)?)
So, in your case you'd end up with a presentPicker function looking something like this (notice the last line):
func presentPicker() {
mediaPicker = MPMediaPickerController(mediaTypes: .music)
if let picker = mediaPicker {
picker.delegate = self
picker.allowsPickingMultipleItems = false
picker.showsCloudItems = false
picker.prompt = "Please Pick a Song"
present(picker, animated: false, completion: nil)
}
}
Hope that helps you.
Swift 5, Swift 4 Very simple solution
//MARK:- add it in your info.plist file
<key>NSAppleMusicUsageDescription</key>
<string>$(app Name) uses music</string>
//MARK:- Import in your file
import MediaPlayer
//MARK:- Import as your Delegates
MPMediaPickerControllerDelegate
//MARK:- Just call this function
func openAudio()
{
let audiopicker = MPMediaPickerController(mediaTypes: .anyAudio)
audiopicker.prompt = "Audio"
audiopicker.delegate = self
audiopicker.allowsPickingMultipleItems = false
self.present(audiopicker, animated: true, completion: nil)
}
Related
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
}
class CameraPicker: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
weak var viewController:MyProfileVC!
func launchCamera() {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.camera) {
let imagePicker:UIImagePickerController = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.camera
imagePicker.cameraDevice = UIImagePickerControllerCameraDevice.front
imagePicker.cameraCaptureMode = .photo
imagePicker.allowsEditing = false
self.viewController.present(imagePicker, animated: true, completion: nil)
} }
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print("didFinishPickingMedia")
}
This is my object class function, but 'didFinishPickingMediaWithInfo' function doesn't get called after taking the picture. Also, the viewcontroller which is presenting the imagepicker is a different Swift file
I had the same problem and I've found a solution, so I'm posting my version (I'm taking a picture from the photo library but it's the same :) ).
I had a memory management issue.
I've created an IBAction function where I instantiated my camera handler class (with the delegate inside...). At the end of the function the variable goes out of scope and it's deallocated. To solve the issue I've made it as instance variable.
That's my code for the VC with my UiButton:
class STECreateUserVC: UIViewController {
#IBOutlet weak var imgAvatar: UIImageView!
let cameraHandler = STECameraHandler()
#IBAction func buttonPressed(_ sender: UIButton) {
cameraHandler.importPictureIn(self) { [weak self] (image) in
self?.imgAvatar.image = image
}
}
}
...and that's my handler:
class STECameraHandler: NSObject {
let imagePickerController = UIImagePickerController()
var completitionClosure: ((UIImage) -> Void)?
func importPictureIn(_ viewController: UIViewController, completitionHandler:((UIImage) -> Void)?) {
completitionClosure = completitionHandler
imagePickerController.delegate = self
imagePickerController.allowsEditing = true
imagePickerController.sourceType = .photoLibrary
imagePickerController.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)!
viewController.present(imagePickerController, animated: true, completion: nil)
}
}
extension STECameraHandler: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let completitionClosure = completitionClosure, let image = info[UIImagePickerControllerEditedImage] as? UIImage {
completitionClosure(image)
}
imagePickerController.dismiss(animated: true)
}
}
I've used a closure in order to have a cleaner code.
This:
self.viewController.present(imagePicker, animated: true, completion: nil)
should be:
self.present(imagePicker, animated: true, completion: nil)
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)
})
}
}
}
I have a profile class and settings class
profile class contains an internal function
class Profile: UIViewController,UIImagePickerControllerDelegate,UINavigationControllerDelegate {
internal func profileSelectFromGallery(sender: Profile){
let myPickerController = UIImagePickerController()
myPickerController.delegate = sender;
myPickerController.sourceType =
UIImagePickerControllerSourceType.PhotoLibrary
sender.presentViewController(myPickerController, animated:true, completion: nil)
}
}
I want to use profileSelectFromGallery in setting class and I have two tries below
class SettingsVC: UITableViewController {
// I will call this private function on a click events
private func selectFromGallery(){
// let profile = Profile()
// profile.profileSelectFromGallery(self)
Profile.profileSelectFromGallery(self)
}
}
The above codes results into Cannot convert value of type 'SettingsVC' to expected argument type 'Profile' since profileSelectFromGallery needs a parameter of a class Profile so what i want to do is change sender so that i can use it from any of my class and not just my Profile class.
So the problem is that you can't convert a SettingsVC into a Profile. If you look at the method signature you'll see it's expecting a Profile:
internal func profileSelectFromGallery(sender: Profile)
You are trying to pass in a SettingVC in selectFromGallery()
Inside profileSelectFromGallery you want the sender to be both a UIViewController and a UIImagePickerControllerDelegate. There's a couple ways you could do this:
The simplest is to change the method signature. You'd do something like this:
internal func profileSelectFromGallery(sender: UIImagePickerControllerDelegate){
guard let vc = sender as? UIViewController else {
return
}
let myPickerController = UIImagePickerController()
myPickerController.delegate = sender;
myPickerController.sourceType =
UIImagePickerControllerSourceType.PhotoLibrary
vc.presentViewController(myPickerController, animated:true, completion: nil)
}
Theres 2 main things here: sender is changed to the proper delegate method, and theres a guard statement to cast it to a VC for the presentViewController call.
The much more awesome way to do this is to use protocol extensions!
extension UIImagePickerControllerDelegate where Self: UIViewController, Self: UINavigationControllerDelegate {
func profileSelectFromGallery() {
let myPickerController = UIImagePickerController()
myPickerController.delegate = self
myPickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
self.presentViewController(myPickerController, animated:true, completion: nil)
}
}
Basically what I'm doing here is adding a method for every UIImagePickerControllerDelegate thats also an UIViewController and an UINAvigationControllerDelegate. This means that I can call it on both Profile and SettingVC (once you add the necessary delegates to SettingVC). All you would need to do is:
let profile = Profile()
profile.profileSelectFromGallery()
let settingVC = SettingVC()
settingVC.profileSelectFromGallery()
Declare new protocol as:
protocol PickerProtocol : UIImagePickerControllerDelegate, UINavigationControllerDelegate {
}
Now your Profile class will look like:
class Profile: UIViewController, PickerProtocol {
//Option 1
internal func profileSelectFromGallery(contoller: UIViewController, pickerProtocol: PickerProtocol){
let myPickerController = UIImagePickerController()
myPickerController.delegate = pickerProtocol
myPickerController.sourceType =
UIImagePickerControllerSourceType.PhotoLibrary
contoller.presentViewController(myPickerController, animated:true, completion: nil)
}
//Option 2
internal func profileSelectFromGalleryOption2(sender : UIViewController? ) {
var viewContoller : UIViewController = self
if let unwrappedSender = sender {
viewContoller = unwrappedSender
}
let myPickerController = UIImagePickerController()
if let pickerProtocol = viewContoller as? PickerProtocol {
myPickerController.delegate = pickerProtocol
} else {
myPickerController.delegate = self //Assign self as default
}
myPickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
viewContoller.presentViewController(myPickerController, animated:true, completion: nil)
}
}
class SettingsVC1: UITableViewController {
// I will call this private function on a click events
private func selectFromGallery(){
let profile = Profile()
profile.profileSelectFromGallery(self, pickerProtocol:profile)
profile.profileSelectFromGalleryOption2(self)
//Or
profile.profileSelectFromGalleryOption2(nil)//profile itself delegate and presenting controller
}
}
// OR
class SettingsVC2: UITableViewController, PickerProtocol {
// I will call this private function on a click events
private func selectFromGallery(){
let profile = Profile()
profile.profileSelectFromGallery(self, pickerProtocol:self)
profile.profileSelectFromGalleryOption2(self)
//Or
profile.profileSelectFromGalleryOption2(nil)//profile itself delegate and presenting controller
}
}
I would use POP (protocol oriented programming) like this:
protocol SelectorProtocol: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
}
extension SelectorProtocol where Self: UIViewController {
func profileSelectFromGallery() {
let myPickerController = UIImagePickerController()
myPickerController.delegate = self;
myPickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
self.presentViewController(myPickerController, animated:true, completion: nil)
}
}
class Profile: UIViewController,UIImagePickerControllerDelegate,UINavigationControllerDelegate, SelectorProtocol {
func foo() {
profileSelectFromGallery()
}
}
class SettingsVC: UITableViewController, SelectorProtocol {
// I will call this private function on a click events
private func selectFromGallery(){
profileSelectFromGallery()
}
}
You are trying to statically call profileSelectFromGallery: even though it is an instance method.
Try changing the method definition to:
internal static func profileSelectFromGallery(sender: Profile){
As for being able to use any class as a delegate, create a custom Protocol and ensure that the sender conforms to this protocol. See here (specifically the heading titled Protocols) for more information: http://www.raywenderlich.com/115300/swift-2-tutorial-part-3-tuples-protocols-delegates-and-table-views
perhaps the following will work:
class SettingsVC: UITableViewController {
// I will call this private function on a click events
private func selectFromGallery(){
let prof = Profile()
prof.profileSelectFromGallery(prof)
}
}
When I choose to print out MPMediaItemCollection in my app I simply receive codes such as 0x17eb5d30. Is anybody aware of how to get data from these random letters and numbers. I am looking at hopefully retrieving the title of the song as well as the length of the song in seconds.
My code is here
#IBAction func pickSong(sender: AnyObject) {
self.presentPicker(sender)
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var DestView: playMusicViewController = segue.destinationViewController as! playMusicViewController
DestView.selectedSong = MPMediaItemCollection()
}
}
func presentPicker (sender:AnyObject) {
let mediaPicker = MPMediaPickerController(mediaTypes: .Music)
mediaPicker.delegate = self
mediaPicker.allowsPickingMultipleItems = false
presentViewController(mediaPicker, animated: true, completion: {})
}
also the extension view controller
extension ViewController : MPMediaPickerControllerDelegate {
// must implement these, as there is no automatic dismissal
func mediaPicker(mediaPicker: MPMediaPickerController!, didPickMediaItems mediaItemCollection: MPMediaItemCollection!) {
let player = MPMusicPlayerController.applicationMusicPlayer()
player.setQueueWithItemCollection(mediaItemCollection)
player.play()
println(mediaItemCollection)
self.dismissViewControllerAnimated(true, completion: nil)
}
func mediaPickerDidCancel(mediaPicker: MPMediaPickerController!) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
The number and letter you are seem is a pointer to an address in memory (normally not very useful for us) if you want to access details about the music you need to access the properties of the objects you received back for the picker. The example below show how to retrieve the title of the music selected:
func mediaPicker(mediaPicker: MPMediaPickerController!, didPickMediaItems mediaItemCollection: MPMediaItemCollection!) {
let player = MPMusicPlayerController.applicationMusicPlayer()
player.setQueueWithItemCollection(mediaItemCollection)
player.play()
let item = mediaItemCollection.representativeItem
let title = item.title
println(title)
self.dismissViewControllerAnimated(true, completion: nil)
}
I hope that help you!