I am working on my first application, its a mathematical riddles app. The player can get a hint that will reveal one of the variables - it's basically replacing one image with another. Sometimes I am replacing more than one image so I am using a loop that replace all of them. I want the old image to fade and be replaced with the new image, the answer. Also I would like them to fade one after the other, meaning that there will be a small delay between one image replacement animation to the next.
func changeHintIcons () {
var labelsArr = [[firstEquationFirstElemnt,firstEquationSecondElemnt,firstEquationThirdElemnt],[secondEquationFirstElemnt,secondEquationSecondElemnt,secondEquationThirdElemnt],[thirdEquationFirstElemnt,thirdEquationSecondElemnt,thirdEquationthirdElemnt],[fourthEquationFirstElemnt,fourthEquationSecondElemnt,fourthEquationThirdElemnt], [fifthEquationFirstElemnt,fifthEquationSecondElemnt,fifthEquationThirdElemnt]]
let col:Int = Int(arc4random_uniform(UInt32(gameDifficulty.stages[gameLevel].umberOfVariables)))
let row:Int = Int(arc4random_uniform(UInt32(2))) * 2
let var_to_show = current_equations[col][row]
let image_name = "answer.num.\(var_to_show)"
for i in 0..<current_equations.count {
for j in 0..<current_equations[i].count {
if (current_equations[i][j] == var_to_show) {
var image_index = j
if (j > 0) {
image_index = Int(j/2) //Converting index
}
labelsArr[i][image_index]!.image = UIImage(named: image_name)! //Replacing the image
}
}
}
}
One last thing, what if I want to use in an animation instead of letting the image simply fade out? What are my options and how can I implement them?
Ok I found the answer. Basically swift allows you to create your animation by projecting a set of images one after the other. Follow these steps:
1. Copy animation images to assets folder
2. create an array of UIImages
3. Do the same things as I did in the animate function
Main code -
var animationArray = createImageArray(total: 14, imagePrefix: "hint.animation")
animationArray.append(UIImage(named: imageHintAnswer)!)
animate(imageView: labelsArr[i][image_index]!, images: animationArray)
Functions -
func createImageArray(total: Int, imagePrefix: String) -> [UIImage] {
var imageArray:[UIImage] = []
for imageCount in 1..<total {
let imageName = "\(imagePrefix).\(imageCount)"
let image = UIImage(named: imageName)!
imageArray.append(image)
}
return imageArray
}
func animate(imageView: UIImageView, images: [UIImage]) {
imageView.animationImages = images
imageView.animationDuration = 0.7
imageView.animationRepeatCount = 1
imageView.startAnimating()
}
Related
A gif image is loaded into a UIImageView (by using this extension) and another UIImageView is overlaid on it. Everything works fine but the problem is when I going for combine both via below code, it shows a still image (.jpg). I wanna combine both and after combine it should be a animated image (.gif) too.
let bottomImage = gifPlayer.image
let topImage = UIImage
let size = CGSize(width: (bottomImage?.size.width)!, height: (bottomImage?.size.height)!)
UIGraphicsBeginImageContext(size)
let areaSize = CGRect(x: 0, y: 0, width: size.width, height: size.height)
bottomImage!.draw(in: areaSize)
topImage!.draw(in: areaSize, blendMode: .normal, alpha: 0.8)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Click here to know more about this problem please.
When using an animated GIF in a UIImageView, it becomes an array of UIImage.
We can set that array with (for example):
imageView.animationImages = arrayOfImages
imageView.animationDuration = 1.0
or, we can set the .image property to an animatedImage -- that's how the GIF-Swift code you are using works:
if let img = UIImage.gifImageWithName("funny") {
bottomImageView.image = img
}
in that case, the image also contains the duration:
img.images?.duration
So, to generate a new animated GIF with the border/overlay image, you need to get that array of images and generate each "frame" with the border added to it.
Here's a quick example...
This assumes:
you are using GIF-Swift
you have added bottomImageView and topImageView in Storyboard
you have a GIF in the bundle named "funny.gif" (edit the code if yours is different)
you have a "border.png" in assets (again, edit the code as needed)
and you have a button to connect to the #IBAction:
import UIKit
import ImageIO
import UniformTypeIdentifiers
class animImageViewController: UIViewController {
#IBOutlet var bottomImageView: UIImageView!
#IBOutlet var topImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
if let img = UIImage.gifImageWithName("funny") {
bottomImageView.image = img
}
if let img = UIImage(named: "border") {
topImageView.image = img
}
}
#IBAction func saveButtonTapped(_ sender: Any) {
generateNewGif(from: bottomImageView, with: topImageView)
}
func generateNewGif(from animatedImageView: UIImageView, with overlayImageView: UIImageView) {
var images: [UIImage]!
var delayTime: Double!
guard let overlayImage = overlayImageView.image else {
print("Could not get top / overlay image!")
return
}
if let imgs = animatedImageView.image?.images {
// the image view is using .image = animatedImage
// unwrap the duration
if let dur = animatedImageView.image?.duration {
images = imgs
delayTime = dur / Double(images.count)
} else {
print("Image view is using an animatedImage, but could not get the duration!" )
return
}
} else if let imgs = animatedImageView.animationImages {
// the image view is using .animationImages
images = imgs
delayTime = animatedImageView.animationDuration / Double(images.count)
} else {
print("Could not get images array!")
return
}
// we now have a valid [UIImage] array, and
// a valid inter-frame duration, and
// a valid "overlay" UIImage
// generate unique file name
let destinationFilename = String(NSUUID().uuidString + ".gif")
// create empty file in temp folder to hold gif
let destinationURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(destinationFilename)
// metadata for gif file to describe it as an animated gif
let fileDictionary = [kCGImagePropertyGIFDictionary : [kCGImagePropertyGIFLoopCount : 0]]
// create the file and set the file properties
guard let animatedGifFile = CGImageDestinationCreateWithURL(destinationURL as CFURL, UTType.gif.identifier as CFString, images.count, nil) else {
print("error creating file")
return
}
CGImageDestinationSetProperties(animatedGifFile, fileDictionary as CFDictionary)
let frameDictionary = [kCGImagePropertyGIFDictionary : [kCGImagePropertyGIFDelayTime: delayTime]]
// use original size of gif
let sz: CGSize = images[0].size
let renderer: UIGraphicsImageRenderer = UIGraphicsImageRenderer(size: sz)
// loop through the images
// drawing the top/border image on top of each "frame" image with 80% alpha
// then writing the combined image to the gif file
images.forEach { img in
let combinedImage = renderer.image { ctx in
img.draw(at: .zero)
overlayImage.draw(in: CGRect(origin: .zero, size: sz), blendMode: .normal, alpha: 0.8)
}
guard let cgFrame = combinedImage.cgImage else {
print("error creating cgImage")
return
}
// add the combined image to the new animated gif
CGImageDestinationAddImage(animatedGifFile, cgFrame, frameDictionary as CFDictionary)
}
// done writing
CGImageDestinationFinalize(animatedGifFile)
print("New GIF created at:")
print(destinationURL)
print()
// do something with the newly created file...
// maybe move it to documents folder, or
// upload it somewhere, or
// save to photos library, etc
}
}
Notes:
the code is based on this article: How to Make an Animated GIF Using Swift
this should be considered Example Code Only!!! -- a starting-point for you, not a "production ready" solution.
I have three pictures (lvl1.png, lvl2.png, lvl3.png) and an variable (let level = 1). What should I do to display an image named 'lvl2' if level = 2, and when level = 3 I need to show the last image (lvl3.png)?
you can use:
let image = UIImage(named: "lvl\(level).png")
or
let image: UIImage!
switch level {
case 1:
image = UIImage(named: "lvl1.png")
case 2:
image = UIImage(named: "lvl2.png")
case 3:
image = UIImage(named: "lvl3.png")
default:
image = UIImage()
}
avatar.image = image
you can try
imgName = "lvl" + String(lavel)
imageView.image = UIImage(named: imgName)
Another alternative is to create a method that returns an image, like so:
func imageFor(level: Int) -> UIImage? {
let image = UIImage(named: "lvl\(currentLevel)")
return image
}
Usage:
var currentLevel = 1
let image = imageFor(level: currentLevel)
You can try
var level = 1 {
didSet {
self.imageview.image = UIImage(named: "lvl\(level)")
}
}
when you change the level the imageView will change automatically
This question already has answers here:
Swift - How to animate Images?
(7 answers)
Closed 6 years ago.
I am working on the UIImageView transition in swift. I an storing six images in an array and giving that array to “imageview.animationImages
” and I am trying to making it to work as ‘Gif animation’ the code I wrote is as follows :
logoImages = NSMutableArray(array: ["backGround4.jpeg","backGround3.jpeg","backGround.jpeg","16.jpeg","23.jpeg","backGround1.jpeg"])
imageview.animationImages = NSArray(array: ["backGround4.jpeg","backGround3.jpeg","backGround.jpeg","16.jpeg","23.jpeg","backGround1.jpeg"]) as? [UIImage]
imageview.animationDuration = 1.5
imageview.animationRepeatCount = 1
imageview.startAnimating()
It is not working.Can anyone please tell me what is the mistake I am doing here?
Thanks In Advance
First correct your image name like backGround1.jpeg, backGround2.jpeg etc..
After that create one image array which will hold all your images:
var imageArray = [UIImage]()
Then add all images in image array:
for var i = 1; i < totalImageCount; i++ {
let image = UIImage(named: "backGround4\(i).png")
imageArray.append(image!)
}
After that you can create animation this way:
imageview.animationImages = imageArray
imageview.animationDuration = 1.5
imageview.startAnimating()
Please convert string to UIImage like this:
imageview.animationImages = NSArray(array: ["backGround4.jpeg","backGround3.jpeg","backGround.jpeg","16.jpeg","23.jpeg","backGround1.jpeg"].map({ (string) -> UIImage in
UIImage(named: string)! })) as? [UIImage]
Please try this
var images: [UIImage] = []
for i in 1...2 {
images.append(UIImage(named: "c\(i)")!)
}
myImageView.animationImages = images
myImageView.animationDuration = 1.0
myImageView.startAnimating()
In the function below(didPressTakePhoto), I am trying to take a series of pictures(10 in this case), store them into an array and display them as an animation in the "gif". Yet the program keeps crashing and I have no idea why. This is all after one button click, hence the function name. Also, I tried taking the animation code outside the for loop, but the imageArray would then lose it's value for some reason.
func didPressTakePhoto(){
if let videoConnection = stillImageOutput?.connectionWithMediaType(AVMediaTypeVideo){
videoConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
stillImageOutput?.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: {
(sampleBuffer, error) in
//var counter = 0
if sampleBuffer != nil {
for var index = 0; index < 10; ++index {
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
let dataProvider = CGDataProviderCreateWithCFData(imageData)
let cgImageRef = CGImageCreateWithJPEGDataProvider(dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)
var imageArray: [UIImage] = []
let image = UIImage(CGImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.Right)
imageArray.append(image)
imageArray.insert(image, atIndex: index++)
self.tempImageView.image = image
self.tempImageView.hidden = false
//UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
var gif: UIImageView!
gif.animationImages = imageArray
gif.animationRepeatCount = -1
gif.animationDuration = 1
gif.startAnimating()
}
}
})
}
}
Never try to make an array of images (i.e., a [UIImage] as you are doing). A UIImage is very big, so an array of many images is huge and you will run out of memory and crash.
Save your images to disk, maintaining only references to them (i.e. an array of their names).
Before using your images in the interface, reduce them to the actual physical size you will need for that interface. Using a full-size image for a mere screen-sized display (or smaller) is a huge waste of energy. You can use the ImageIO framework to get a "thumbnail" smaller version of the image from disk without wasting memory.
You are creating a new [UIImage] in every iteration of the loop, so in the last iteration there's only one image, you should take the imageArray creation out of the loop. Having said that, you should take into account what #matt answered
I am a beginner programmer and am creating a game using iOS sprite-kit. I have a simple animated GIF (30 frames) saved as a .gif file. Is there a simple way (few lines of code maybe similar to adding a regular .png through UIImage) of displaying this GIF in my game? I have done some research on displaying an animated GIF in Xcode and most involve importing extensive classes, most of which is stuff I don't think I need (I barely know enough to sift through it).
The way I think of it gifs are just like animating a sprite. So what I would do is add the gif as textures in a SKSpriteNode in a for loop and then tell it to run on the device with SKAction.repeatActionForever().
To be honest I'm fairly new to this as well. I'm just trying to give my best answer. This is written in Swift, but I don't think it'll be to hard to translate to Objective-C.
var gifTextures: [SKTexture] = [];
for i in 1...30 {
gifTextures.append(SKTexture(imageNamed: "gif\(i)"));
}
gifNode.runAction(SKAction.repeatActionForever(SKAction.animateWithTextures(gifTextures, timePerFrame: 0.125)));
Michael Choi's answer will get you half way there. The rest is getting the individual frames out of the gif file. Here's how I do it (in Swift):
func load(imagePath: String) -> ([SKTexture], TimeInterval?) {
guard let imageSource = CGImageSourceCreateWithURL(URL(fileURLWithPath: imagePath) as CFURL, nil) else {
return ([], nil)
}
let count = CGImageSourceGetCount(imageSource)
var images: [CGImage] = []
for i in 0..<count {
guard let img = CGImageSourceCreateImageAtIndex(imageSource, i, nil) else { continue }
images.append(img)
}
let frameTime = count > 1 ? imageSource.delayFor(imageAt: 0) : nil
return (images.map { SKTexture(cgImage: $0) }, frameTime)
}
extension CGImageSource { // this was originally from another SO post for which I've lost the link. Apologies.
func delayFor(imageAt index: Int) -> TimeInterval {
var delay = 0.1
// Get dictionaries
let cfProperties = CGImageSourceCopyPropertiesAtIndex(self, index, nil)
let gifPropertiesPointer = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: 0)
if CFDictionaryGetValueIfPresent(cfProperties, Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque(), gifPropertiesPointer) == false {
return delay
}
let gifProperties: CFDictionary = unsafeBitCast(gifPropertiesPointer.pointee, to: CFDictionary.self)
// Get delay time
var delayObject: AnyObject = unsafeBitCast(
CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()),
to: AnyObject.self)
if delayObject.doubleValue == 0 {
delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)
}
delay = delayObject as? TimeInterval ?? 0.1
if delay < 0.1 {
delay = 0.1 // Make sure they're not too fast
}
return delay
}
}
Note that I assume that each frame of the gif is the same length, which is not always the case.
You could also pretty easily construct an SKTextureAtlas with these images.