I am trying to make it so that when someone touches a cell in this UITableView, that a song will begin to play. Each cell has an MPMediaItem associated with it. I want to play the songs using an AVAudioPlayer. I am trying to get the url of the MPMediaItem and use it to get the song for the AVAudioPlayer.
var songsList: [MPMediaItem] = MPMediaQuery.songsQuery().items!
var player = AVPlayer()
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let song = songsList[indexPath.section]
if let url = song.valueForProperty(MPMediaItemPropertyAssetURL) as! NSURL? {
let newPlayerItem = AVPlayerItem(URL: url)
player.replaceCurrentItemWithPlayerItem(newPlayerItem)
player.play()
}
else {
print("Failed to cast to URL")
}
}
The variable url fails to cast as NSURL and I cannot figure out why.
Thanks for any help
After many tests, I found the problem is you can't cast a non-optional value to an optional value. So delete ? after NSURL in your origin code. May it helps.
Related
I am trying to load pdf in webView but api link is come from api and I want to open that link in webView which is actually on another viewController so I am passing link to that view and load into webView but I am getting error like this:
Could not cast value of type '__NSCFString' (0x1032a04f0) to 'NSURL' (0x1032a21b0).
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let view: PDFViewController = self.storyboard?.instantiateViewController(withIdentifier: "PDFViewController") as! PDFViewController
let pdf = eCataLogList[indexPath.row]["pdf_file"]
let request = NSURLRequest(url: (pdf as? URL)!)
// let request = NSURLRequest(url: pdf as! URL);
view.pdf_web.loadRequest(request as URLRequest)
self.navigationController?.pushViewController(view, animated: true)
}
You need to first convert your pdf string to url. Use following code,
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let view: PDFViewController = self.storyboard?.instantiateViewController(withIdentifier: "PDFViewController") as! PDFViewController
let pdf = eCataLogList[indexPath.row]["pdf_file"]
let request = URLRequest(url: URL(string: pdf))
view.pdf_web.loadRequest(request)
self.navigationController?.pushViewController(view, animated: true)
}
Good point you check the error in debugger (debutants don't do it often).
Could not cast value of type '__NSCFString' (0x1032a04f0) to 'NSURL'
(0x1032a21b0).
Let's translate it:
At some point in your code, you are trying to cast an NSString object (That's the part __NSCFString) into a NSURL. That means, that you are trying to tell the compiler that this String can be a NSURL, and it's telling you NO at runtime.
Here is the line: (pdf as? URL)!
Ask you this:
Can a String object be translated as such as an URL object? Can a UIImage be translated as such a CBPeripheral?
No, not really.?
Classes/structs have meaning, different goal and different uses.
Quick fix:
let pdfURLString = eCataLogList[indexPath.row]["pdf_file"] //So that's a String
let pdfURL = URL.init(string: pdfURLString)
let request = NSURLRequest(url: pdfURL!)
Now let's review more your code:
Avoid using NSStuff when there is a "Swift" version: NS(Mutable)URLRequest => URLRequest, etc. Of course, you can use the bridges and cast with a as a URLRequest into a NSURLRequest and reverse, but see that in your current code you are doing NSURLRequest to URLRequest, while you could have use URLRequest from the start. That's just adding noises to do each time as.
Avoid force unwrapping when possible, that's the use of !. If it fails (it's nil), it will crash. Instead use guard let or if let
guard let view: PDFViewController = self.storyboard?.instantiateViewController(withIdentifier: "PDFViewController") as? PDFViewController else {
print ("Error in initializing the PDFViewController")
return
}
let pdfURLString = eCataLogList[indexPath.row]["pdf_file"]
if let pdfURL = URL.init(string: pdf) {
let request = URLRequest(url: pdfURL)
view.pdf_web.loadRequest(request)
self.navigationController?.pushViewController(view, animated: true)
} else {
print("Ooops, \(pdfURLString ?? "pdfURLString for row \(indexPath.row)") doesn't seem to be a valid URL")
}
Note:
I explicitly wrote URL.init(string: someURLString), but you can write URL(string: someURLString).
I am creating a social network app in order to learn swift (using Swift 4) and complete a project . I have a TableView that shows videos and I have added the functionality of 'liking' videos like any social network. My issue is that when you like a video the TableView gets reloaded to show the '+ 1 like' and the Video starts all over again. How can I make it so that the video doesn't restart every time you like a video . This is my code here
1st You get the user clicking the Like Action which sends a call to the database and insert the like and add '+1 to the like field'
#IBAction func LikeAction(_ sender: UIButton) {
DontReload = sender.tag
let url:URL = URL(string:ConnectionString+"insert_like")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let parameter = "parameters"
request.httpBody = parameter.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil
{
print("error=\(String(describing: error))")
return
}
DispatchQueue.main.async {
self.reloadTable()
}
}.resume()
}
Then I query the database and return the new data showing the +1 like and other data in Json Format.
func reloadTable() {
var url = URL(string:ConnectionString+"streams")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let parameter = "Parameters"
request.httpBody = parameter.data(using: String.Encoding.utf8)
session.dataTask(with:request, completionHandler: {(data, response, error) in
if error != nil {
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
if let Streams = parsedData["Results"] as? [AnyObject]? {
// check for misspelled words
if streamsModel.Locations.count >= 0 {
// Set My Arrays
}
for Stream in Streams! {
// Gets Json Values
}
TableSource.reloadData()
}
}
else {
DispatchQueue.main.async {
streamsModel.Locations.removeAll()
TableSource.reloadData()
}
} catch let error as NSError {
print(error)
}
}
}).resume()
}
This is my TableViewCell and this is obviously called to show the new updated data, however if the user is watching a video and likes it while it is playing then the video restarts... any suggestions on solving this would be great.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTVC", for: indexPath) as! HomeTVC
// Starts Video Plays
cell.videoView = cell.VideoViewArray[indexPath.row]
cell.videoView.tag = indexPath.row
let movieURL = URL(string: cell.stream_image_string[indexPath.row])
cell.videoCapHeight.constant = CGFloat(Float(cell.pic_height!))
cell.playerView = AVPlayer(url: movieURL!)
cell.MyAVPlayer.player = cell.playerView
cell.MyAVPlayer.videoGravity = AVLayerVideoGravity.resizeAspectFill.rawValue
cell.MyAVPlayer.showsPlaybackControls = false
cell.MyAVPlayer.view.frame = cell.videoView.bounds
cell.videoView.addSubview(cell.MyAVPlayer.view)
controller.addChildViewController(cell.MyAVPlayer)
cell.playerView?.isMuted = false
cell.MyAVPlayer.player?.play()
// Ends Video play
return cell
}
Again my code works the only issue is that my videos restart on Table Reloads . I want to create some type of condition or flag that If a Table View Cell gets 'liked' and there is a video then I want that video to not get reloaded . Any suggestions would be great . Based on suggestions below I will stop using ReloadTable and attempt to grab a reference for that UIButton perhaps something like this
let indexPath = NSIndexPath()
let cell = self.TableSource.dequeueReusableCell(withIdentifier: "HomeTVC", for: indexPath as IndexPath) as! HomeTVC
cell.votes.setTitle("result from server",for: UIControlState.normal)
As per your requirement is seems you don't need to reload whole table view in case of just update like, Once you receive API response of "insert_like" You can update your array and directly get reference of your video running cell and update data source of it.
i have this code for download PDF file :
var documents = [PDFDocument]()
DispatchQueue.global(qos: .default).async(execute: {
//All stuff here
print("Download PDF");
let url=NSURL(string: urlString);
let urlData=NSData(contentsOf: url! as URL);
if((urlData) != nil)
{
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let fileName = urlString as NSString;
let filePath="\(documentsPath)/\(fileName.lastPathComponent)";
let fileExists = FileManager().fileExists(atPath: filePath)
if(fileExists){
// File is already downloaded
print("PDF Already Downloaded");
}
else{
//download
DispatchQueue.main.async(execute: { () -> Void in
print(filePath)
urlData?.write(toFile: filePath, atomically: true);
print("PDF Saved");
self.refreshData()
})
}
}
})
Now I want to remove this file from uitableview in table and in documentdirecotry how to use index path row and how to find file name for removing
i know i will remove the file here but i don't know how to exactly remove the PDF in documentDirectory and Table
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
// handle delete (by removing the data from your array and updating the tableview)
}
}
here is my table view cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! BookshelfCell
let document = documents[indexPath.row]
if let documentAttributes = document.documentAttributes {
if let title = documentAttributes["Title"] as? String {
cell.title = title
}
if let author = documentAttributes["Author"] as? String {
cell.author = author
}
here is my refresh data part
let fileManager = FileManager.default
let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let contents = try! fileManager.contentsOfDirectory(at: documentDirectory, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
documents = contents.flatMap { PDFDocument(url: $0) }
You will need to complete three steps to properly delete the document and update your table view:
Use FileManager's removeItem(at: URL) or removeItem(atPath: String) to delete the file from disk. (note that both of these methods throw so you need to use a do-catch block along with try and only proceed if the method didn't throw an error.) Update: If you look at the documentation for PDFDocument you will find that in addition to the documentAttributes that you are already using there is another optional property, documentURL that should give you exactly what you need to remove it.
Remove the document from documents (you could just refresh the whole array using your existing code but removing a single item is faster). documents.remove(at: indexPath.row)
Finally, you need to tell the table view to remove the row in question (you could of course just reload the whole table view but removing the single cell is cleaner) tableView.deleteRows(at: [indexPath], with .fade)
In case you are unfamiliar with do-catch blocks here is a bit of code from Apple's book on Swift (see below for link) simplified a bit:
do {
try makeASandwich()
eatASandwich() // This only gets called if the line above worked
} catch {
dealWithTheError() // This only gets called if makeASandwich() throws an error
}
Side Note
Apple has a fantastic guide on the Swift Language if you haven't done so yet I suggest reading, at least, The Basics. This will give you a base understanding of the language. If you are also new to programming I would suggest going through Apple's Learn to Code series that is free on iPads in the Swift Playgrounds app. The series will guide you through all the basics of programming giving you the tools to search through the documentation that Apple provides and find answers to your questions.
We all started at the beginning at some point, and we all had to crawl before we could walk and well before we could run.
So I have a list of audio files and when a cell is pressed it plays the audio. This works fine but I am not able to pause it by clicking the same cell again.
My code for the table view:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
let a = indexPath.section;
let b = indexPath.row;
var path = NSBundle.mainBundle().pathForResource(musicArray[b], ofType: "mp3")
var error:NSError?
do{
audioPlayer = try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: path!))
}
catch
{
print("Something bad happened")
}
if (audioPlayer.playing)
{
audioPlayer.pause();
}
else
{
audioPlayer.play();
}
}
musicArray has the titles of the audio files
You're creating a new AVAudioPlayer every time you select the cell. You need to keep a reference to the previous one and tell that audio player to pause.
You can create a singleton object.
import AVFoundation
class SomeAudioManager: NSObject, AVAudioPlayerDelegate
{
class var sharedInstance: SomeAudioManager {
struct Static {
static var onceToken: dispatch_once_t = 0
static var instance: SomeAudioManager? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = SomeAudioManager()
}
return Static.instance!
}
func audioView(songname: String,format: String) {
let audioPlayer: AVAudioPlayer
do {
audioPlayer = try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(songname, ofType:format)!), fileTypeHint: AVFileTypeMPEGLayer3)
audioPlayer.delegate = self;
audioPlayer.play()
} catch {
// error
}
}
}
It will be alive through the whole app.
How to implement AVAudioPlayer Inside Singleton Method?
You can just create a singleton of the player so that you will always know if it's playing or not.
I'm building a tableview in which each cell represents a sound that is played when the user taps that particular cell.
In objective-C it worked fine, but now that Apple released Swift I decided to move over instantly, but for some reason the sound does not play. My code when the user taps the cell:
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
var currentItem = self.items[indexPath.row]
var audioPath = NSString(string: NSBundle.mainBundle().pathForResource(currentItem.soundID, ofType: "mp3"))
println(audioPath)
var audioPlayer = AVAudioPlayer(contentsOfURL: NSURL(string: audioPath), error: nil)
audioPlayer.play()
}
currentItem is the object in the array that has to be called. Each sound I can play is put in a custom object, together with a title and an image. that object is put in an instance of currentItem.
This is what the printNL outputs when I tapp one of my cells:
/private/var/mobile/Containers/Bundle/Application/ADF0CAFC-4C9E-475E-B3F0-CD85A1873CA5/Juichen.app/StupidQuestion.mp3
it does not give an error. I already tried moving the sound file to other folders, but that does not solve the problem either. therefore, I assume that this problem occurs because I am calling the audioPlayer incorrect?
Any help would be highly appreciated!
Let say you have a class myTable:
class myTable : UITableViewController
{
var audioPlayer:AVAudioPlayer? = nil
...
}
And to initialize audioPlayer:
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!)
{
var currentItem = self.items[indexPath.row]
var audioPath = NSString(string: NSBundle.mainBundle().pathForResource(currentItem.soundID, ofType: "mp3"))
println(audioPath)
var error : NSError? = nil
self.audioPlayer = AVAudioPlayer(contentsOfURL: NSURL(string: audioPath), error: &error)
if (self.audioPlayer == nil)
{
if let playerError = error as? NSError
{
let des : String? = playerError.localizedDescription
println("Error: \(des)")
}
}
else
{
self.audioPlayer.play()
}
}
Hope this helps.