Swift coding generate random number and link to image and sound - xcode

I am very new to both programming and swift (please be kind!). I have a working example of how to generate a random image (by assigning a number and using Int(arc4random_uniform(x)). However I do not know how I can say if this image is shown, then assign this sound to the button. Every time an image is displayed it will have a matching sound to go with it. With my limited experience I can only think of doing long winded if statements?

Answer
Pass the name of the image to a NSMutableString, without the extension (.png, or just remove the extension.). Then (hopefully the audio file has the same name as the images name) you can now call the audio file with from the mutableString.
Explanation
For Example. If you have an image named Dog.png. Name your audio file that goes with the dog image: dog.mp3. (.mp3 is an example). When the user selects the dog image, pass the name to a muatableString. Lets call the mutableString name selectedImage.
Right now, selectedImage should have a value of .png. (Or whatever extension you have.) Remove the last 3 letters of selectedImage like this:
func deleteCharactersInRange(_ aRange: NSRange)
Next you want to call the audio file using selectedImage, and add the extension name like this:
func appendString(_ aString: String)
So selectedImage's value should now be: dog.mp3. And you can now call the audio file with selectedImgae.
Hope this helps!!
Edit
Here is code to play audio files:
var player : AVAudioPlayer! = nil // will be Optional, must supply initializer
let path = NSBundle.mainBundle().pathForResource("audioFile", ofType:"m4a")
let fileURL = NSURL(fileURLWithPath: path)
player = AVAudioPlayer(contentsOfURL: fileURL, error: nil)
player.prepareToPlay()
player.delegate = self
player.play()

Related

Folder of images: create and fill SKShapeNodes, 1 of each

Is it possible to create a folder and have SpriteKit go through that folder, find each image (regardless of name), and create a SKShapeNode for each in that folder (regardless of how many there are)?
Assume all images are the same size, and all .png files ready to be used as unique images as textures for filling SKShapeNodes, and that all SKShapeNodes will be the same size.
I want to know, once done, how many shapes have been created, and have them uniquely named based on the name of the image of the texture used.
But all I'm reading seems to be about knowing how many images are there, and what their names are, before hand.
Clueless as to where to start searching about how to do this.
Update: no idea how to reference the "folder" with images:
I've dumped the images, with their very imaginative names [one, two, three, four, five etc. ], into a folder as "atlas", like this:
but as per Allessandro's wonderful answer, I have no idea how to address this "folder" and get at what's in it.
I think it's this line I'm getting completely wrong:
let folderPath = Bundle.main.path(forResource: "Images", ofType: nil)
I don't know what to replace "Images" with.
Update 2:
Found a much easier way to do this:
let myTextureAtlas = SKTextureAtlas(named: "demoArt")
print(myTextureAtlas)
print(myTextureAtlas.textureNames)
Too many requests , so I try to help you with some code:
Create a folder:
func createDir(folderName:String) {
let fileManager = FileManager.default
if let directory = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "APP_GROUP_IDENTIFIER") {
let newDirectory = directory.appendingPathComponent(folderName)
try! fileManager.createDirectory(at: newDirectory, withIntermediateDirectories: false, attributes: nil)
}
}
Now you have your folder so you can decide to save a file or to copy one file from documents to this new directory. I make an example for the last one.
Copy a file:
let fileManager = FileManager.default
do {
try fileManager.copyItem(atPath: "hero.png", toPath: "subfolder/hero.swift")
}
catch let error as NSError {
print("Something went wrong: \(error)")
}
Now you want to know how to extract all files from a directory.
Extract all files from a directory:
func extractAllFile(atPath path: String, withExtension fileExtension:String) -> [String] {
let pathURL = NSURL(fileURLWithPath: path, isDirectory: true)
var allFiles: [String] = []
let fileManager = FileManager.default
if let enumerator = fileManager.enumerator(atPath: path) {
for file in enumerator {
if #available(iOS 9.0, *) {
if let path = NSURL(fileURLWithPath: file as! String, relativeTo: pathURL as URL).path
, path.hasSuffix(".\(fileExtension)"){
allFiles.append(path)
}
} else {
// Fallback on earlier versions
}
}
}
return allFiles
}
Usage:
let folderPath = Bundle.main.path(forResource: "Images", ofType: nil)
let allPngFiles = extractAllFile(atPath: folderPath!, withExtension: "png") // returns file path of all the png files inside the folder
After that, you can create an array of SKShapeNode based from allPngFiles using fillTexture with a SKTexture created from each image and give to each node the name of the file used (so you know at the end also how many nodes you have created from array.count).
Slightly different answer here. I'm not sure why one needs to create a directory and copy files over. In your screenshot you have a demoArt.atlas with 7 files (one.png, two.png, etc). You indicate you want to generate an SKShapeNode. It still isn't clear to me why you want SKShapeNode, but ignoring that, you just need to know the folder name. You'll notice how the folder is blue, which means it is a reference. This means that in the image, the folder is retained.
This would get you all the files in that directory:
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let texPath = NSURL(fileURLWithPath: documentsPath).appendingPathComponent("demoArt.atlas")
let filemanager:FileManager = FileManager()
// Error check/ensure path exists
let files = filemanager.enumerator(atPath:texPath!.path)
while let file = files?.nextObject() {
print(file)
// Create your SKShapeNode here, you can load file from this
// This is all relative path, so "file" would be one.png, two.png, etc. If you need the full path, you
// need to prepend texPath
}
Note the key diff here is not copying files over. Maybe I just don't understand the problem and why it would require the copy.
If you wanted to put that in an array you can. But here you'll have the file name of the texture which you would of course need to load.
Realistically, you want to load those textures asynchronously first, then construct your SKShapeNode.

Continuous Looping Video in TVOS?

I'm just getting started with TVOS and was wondering if anyone has addressed looping in TVOS / TVJS / TVML. I'm using this tutorial to create a simple interface for playing videos. My wrinkle is that I want these videos to play in a continuous seamless loop - basically a moving screensaver type effect - see example videos at art.chrisbaily.com.
Is there a simple way to do this, or do I need to build some kind of event listener to do the looping manually? I'd like the videos to be fairly hi res, and the videos would be somewhere between 1 and 3 minutes.
I was looking for an answer for this question as well. And found that the only way this would be possible is to create an event listener and adding a duplicate media item to the playlist. But honestly it is not that hard, given if you followed the tutorial that you listed in your post.
So the code would be something like
player.playlist = playlist;
player.playlist.push(mediaItem);
//Again push the same type of media item in playlist so now you have two of the same.
player.playlist.push(mediaItem);
player.present();
This will make sure that once your first video ends the second one starts playing which is essentially a loop. Then for the third one and on you implement an event listener using "mediaItemWillChange" property. This will make sure that once a video ends a new copy of the same video is added to the playlist.
player.addEventListener("mediaItemWillChange", function(e) {
player.playlist.push(mediaItem);
});
Just put the event listener before you start the player using
player.present();
Note this question and idea of this sort was already asked/provided on Apple's discussion board. I merely took the idea and had implemented it in my own project and now knowing that it works I am posting the solution. I found that if I pop the first video out of the playlist and add then push a new on the playlist as mentioned in the linked thread below, my videos were not looping. Below is the link to the original post.
How to repeat video with TVJS Player?
You could also set repeatMode on the playlist object
player.playlist.repeatMode = 1;
0 = no repeat
1 = repeat all items in playlist
2 = repeat current item
There's really no need to push a second media item onto the. Simply listen for the media to reach its end, then set the seek time back to zero. Here's the code.
Play the media.
private func playVideo(name: String) {
guard name != "" else {
return
}
let bundle = NSBundle(forClass:object_getClass(self))
let path = bundle.pathForResource(name, ofType:"mp4")
let url = NSURL.fileURLWithPath(path!)
self.avPlayer = AVPlayer(URL: url)
if (self.avPlayerLayer == nil) {
self.avPlayerLayer = AVPlayerLayer(player: self.avPlayer)
self.avPlayerLayer.frame = previewViews[1].frame
previewViews[1].layer.addSublayer(self.avPlayerLayer)
avPlayer.actionAtItemEnd = .None
//Listen for AVPlayerItemDidPlayToEndTimeNotification
NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerItemDidReachEnd:", name: AVPlayerItemDidPlayToEndTimeNotification, object: self.avPlayer.currentItem)
}
avPlayer.play()
}
Play Again
func playerItemDidReachEnd(notification: NSNotification) {
let item = notification.object as? AVPlayerItem
item?.seekToTime(kCMTimeZero)
}

Photo changing every day(Xcode, Swift)

I haven't write code for this But I want some help that can it be possible to code so that it shows new photo everyday. for example I have 10 images and I want to display each image for like September 16,17,18, and so on ?
Here you go...
Call method getImageForToday() where ever you want to fetch image for today (This solution will work specific to your case, i.e. as you mentioned you have 10 images)
func getImageForToday() -> UIImage
{
let arrImages = ["image1.png", "image2.png" ... "image10.png"]
var imageName = arrImages[getLastCharacterFromTodayDate()]
return UIImage(named: imageName)
}
func getLastCharacterFromTodayDate() -> Int
{
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let component = calendar.components(NSCalendarUnit.CalendarUnitDay, fromDate: NSDate())
return component.day % 10
}
You can use the object NSDate to know what day is it and after that doing something.
First you have your dictionnary of image like that :
let images = [ UIImage(named: "image1.png")!, UIImage(named: "image2.png")!, UIImage(named: "image3.png")]
so now, you must know what day is it for the first app openning in exemple.
So you can do that :
let date = NSDate()
You put the element date in UserDefaults. (in order to save it)
You can now display a image. You make a random number and you take UIImage at this random position.
The day after, or the next app opening you recheck the NSDate with the NSDate saved and if it's different you take a other photo.

How to convert PFObject array in image view?

Sorry guys I want to try this again just reword it so its understandable.
ImageFiles is an array of PFObjects, about 10 images
let randomNumber = imageFiles[Int(arc4random_uniform(UInt32(imageFiles.count)))]
println(randomNumber)
and the println gives me a random image file from the array.
How do I put that into the image view? Meaning i want a random element of the array to be viewable in the uiimage view.
let image = UIImage(data: randomNumber as AnyObject as! NSData)
closest I've got... no syntax error only runtime
Could not cast value of type 'PFObject' (0x105639070) to 'NSData' (0x106c97a48).
An actual image stored in Parse is not of the format PFObject, it is of the type PFFile. Normally a reference to a PFFile is stored within a PFObject.
I can imagine your code/pseudocode will end up looking like the following:
let myRandomImageMetaData = imageFiles[Int(arc4random_uniform(UInt32(imageFiles.count)))]
let imageFileReference: PFFile = myRandomImageMetaData["imgLink"]
The actual code will depend on your database structure which I've already asked you for here, but you never provided.
Now that you have the file reference, then you actually have to download the image data itself. You can do it like so:
imageFileReference.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
}
}
}
You can read more about storing and retrieving images stored as PFFile in Parse here:
https://parse.com/docs/ios/guide#files-images
Now, your next problem is that you have not known all of this while uploading the images in the first place. So there is a high risk that you've uploaded the images to Parse badly in the first place and that the images doesn't exist there at all.

AVPlayer gapless playback and seeking with multiple video files

I'm currently writing a small application which takes a folder containing many short video files (~1mn each) and plays them like they were ONE long video file.
I've been using AVQueuePlayer to play them all one after another but I was wondering if there were an alternative to this, because I'm running into some problems:
there is a small but noticeable gap when the player switches to the next file
I can't go back to the previous video file without having to remove all the items in the queue and put them back
I'd like to be able to go to any point in the video, just as if it were a single video file. Is AVPlayer the best approach for this?
I realize that it's been about 6 years since this was asked, but I found a solution to this shortly after seeing this question and maybe it will be helpful to someone else.
Instead of using a an AVQueuePlayer, I combined the clips in an AVMutableComposition (a subclass of AVAsset) which I could then play in a normal AVPlayer.
let assets: [AVAsset] = urlsOfVideos.map(AVAsset.init)
let composition = AVMutableComposition()
let compositionVideoTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)
let compositionAudioTrack = composition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
var insertTime = CMTime.zero
for asset in assets {
let range = CMTimeRange(start: .zero, duration: asset.duration)
guard let videoTrack = asset.tracks(withMediaType: .video).first,
let audioTrack = asset.tracks(withMediaType: .audio).first else {
continue
}
compositionVideoTrack?.preferredTransform = orientation!
try? compositionVideoTrack?.insertTimeRange(range, of: videoTrack, at: insertTime)
try? compositionAudioTrack?.insertTimeRange(range, of: audioTrack, at: insertTime)
insertTime = CMTimeAdd(insertTime, asset.duration)
}
Then you create the player like this
let player = AVPlayer(playerItem: AVPlayerItem(asset: composition))

Resources