I have an array of big images (4000x4000) and bigger and I'm looping through these images while doing animation.
The goal here is to compress these images before loading them, but no matter what extension I use, calling self.theImage.animationImages does not work.
Here is my code so far:
let images = [
#imageLiteral(resourceName: "imageOne"),
#imageLiteral(resourceName: "imageTwo"),
#imageLiteral(resourceName: "imageThree"),
#imageLiteral(resourceName: "imageFour"),
#imageLiteral(resourceName: "ImageFive"),
#imageLiteral(resourceName: "imageSix")]
self.theImage.animationImages = images;
self.theImage.animationDuration = 10.0
self.theImage.layer.add(rotatePicture, forKey: nil)
self.theImage.startAnimating()
UIView.animate(withDuration: 1, animations:{
self.theImage.frame.origin.y += 440
}){_ in
UIView.animateKeyframes(withDuration: 1, delay: 2.25, options: [.autoreverse, .repeat], animations: {
self.theImage.frame.origin.y -= 490
})
}
}
Try using this method :
func resizeImage(image:UIImage,size:CGSize) ->UIImage
{
let sizeconvert:CGSize = CGSize.init(width: 500, height: 300)
UIGraphicsBeginImageContextWithOptions(size,false,0.0)
image .draw(in: CGRect.init(x: 0, y: 0, width: size.width, height: size.height))
let scaledImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext();
return scaledImage;
}
Related
I want to make it appear as if my image is slowly getting filtered from top to bottom. I am adding two image views. My processed image is in the background and non-processed in the front. I am making the height of non-processed image 0. Here is my code.
imageView.frame = CGRect(x: 0, y: 100, width: self.view.bounds.size.width, height: 400)
imageView.image = processedImage
let nonProcessedImageView = UIImageView()
nonProcessedImageView.frame = CGRect(x: 0, y: 100, width: self.view.bounds.size.width, height: 400)
nonProcessedImageView.image = nonProcessedImage
view.addSubview(nonProcessedImageView)
UIView.transition(with: nonProcessedImageView,
duration: 5.0,
animations: {
nonProcessedImageView.frame = CGRect(x: 0, y: 500, width: self.view.bounds.size.width, height: 0)
},
completion: {_ in
})
The non processed image does not even appear on top of the processed.
The issue seems to be that changing the y coordinate of the frame in the animation block leads to issues when using the UIView.Animate function.
See this answer
To quote the most essential part:
You should change the center property (to move the view) and bounds
property (to change its size) instead. Those properties behave as
expected.
Since you want to just reduce the height, you don't need to do anything to the y coordinate
let nonProcessedImageView = UIImageView()
var newImageFrame = imageView.frame
nonProcessedImageView.frame = newImageFrame
nonProcessedImageView.clipsToBounds = true
nonProcessedImageView.contentMode = .scaleAspectFit
nonProcessedImageView.image = imageView.image
view.addSubview(nonProcessedImageView)
// I set this to 1 instead of 0 as 0 does not show the image
// for some reason
newImageFrame.size.height = 1
// Update your processed image
imageView.image = processedImage
UIView.animate(withDuration: 5.0) {
nonProcessedImageView.frame = newImageFrame
}
completion: { (isComplete) in
if isComplete {
nonProcessedImageView.removeFromSuperview()
}
}
This gives some results but as you can see, the animation is not so good because as you reduce the height, the image view's contentMode kicks in and gives the seen effect.
For best results, add the new image view to a UIView and reduce the height of the UIView instead of the UIImageView
var newImageFrame = imageView.frame
let containerView = UIView(frame: newImageFrame)
containerView.clipsToBounds = true
let nonProcessedImageView = UIImageView()
nonProcessedImageView.frame = containerView.bounds
nonProcessedImageView.clipsToBounds = true
nonProcessedImageView.contentMode = .scaleAspectFit // same as original
nonProcessedImageView.image = imageView.image
containerView.addSubview(nonProcessedImageView)
view.addSubview(containerView)
newImageFrame.size.height = 1
imageView.image = processedImage
UIView.animate(withDuration: 3.0) {
containerView.frame = newImageFrame
}
completion: { (isComplete) in
if isComplete {
containerView.removeFromSuperview()
}
}
This should give you what you want:
This works for top to bottom transition.
imageView.frame = CGRect(x: 0, y: 100, width: self.view.bounds.width, height: 400)
imageView.image = nonProcessedImage
view.addSubview(imageView)
let frontView = UIView(frame: CGRect(x: 0, y: 100, width: self.view.frame.width, height: 0))
frontView.clipsToBounds = true
view.addSubview(frontView)
let nonProcessedImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 400))
nonProcessedImageView.image = processedImage
nonProcessedImageView.clipsToBounds = true
frontView.addSubview(nonProcessedImageView)
UIView.transition(with: frontView, duration: 5, options: [.allowAnimatedContent], animations: {
frontView.frame = CGRect(x: 0, y: 100, width: self.view.frame.width, height: 400)
}, completion: nil)
I know there are several other ways to do this; I don't want to import anything that I don't need to. If someone can help me with his code, that would be great.
Currently, it is only saving the original image without the watermark image.
extension UIImage {
class func imageWithWatermark(image1: UIImageView, image2: UIImageView) -> UIImage {
UIGraphicsBeginImageContextWithOptions(image1.bounds.size, false, 0.0)
image2.layer.renderInContext(UIGraphicsGetCurrentContext()!)
image1.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
}
func addWatermark() {
let newImage = UIImage.imageWithWatermark(imageView, image2: watermarkImageView)
UIImageWriteToSavedPhotosAlbum(newImage, nil, nil, nil)
}
EDIT: I've got the watermark appearing on the saved images.
I had to switch the order of the layers:
image1.layer.renderInContext(UIGraphicsGetCurrentContext()!)
image2.layer.renderInContext(UIGraphicsGetCurrentContext()!)
HOWEVER, it is not appearing in the correct place.It seems to always appear in the center of the image.
If you grab the UIImageViews' images you could use the following concept:
if let img = UIImage(named: "image.png"), img2 = UIImage(named: "watermark.png") {
let rect = CGRect(x: 0, y: 0, width: img.size.width, height: img.size.height)
UIGraphicsBeginImageContextWithOptions(img.size, true, 0)
let context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, UIColor.whiteColor().CGColor)
CGContextFillRect(context, rect)
img.drawInRect(rect, blendMode: .Normal, alpha: 1)
img2.drawInRect(CGRectMake(x,y,width,height), blendMode: .Normal, alpha: 1)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
UIImageWriteToSavedPhotosAlbum(result, nil, nil, nil)
}
SWIFT 4
Use this
let backgroundImage = imageData!
let watermarkImage = #imageLiteral(resourceName: "jodi_url_icon")
let size = backgroundImage.size
let scale = backgroundImage.scale
UIGraphicsBeginImageContextWithOptions(size, false, scale)
backgroundImage.draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
watermarkImage.draw(in: CGRect(x: 10, y: 10, width: size.width, height: size.height - 40))
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
Use result to UIImageView, tested.
I'm not sure how to create this animation. Would you somehow split the 1 jpg file evenly in 3 pieces and animate that? Or would you have to make multiple copies of the jpg and do something with them?
Any help would be awesome!
UPDATE
Since you want a crossfade, it's probably easiest to do this by splitting the image into separate cel images:
import UIKit
import PlaygroundSupport
extension UIImage {
func subImage(inUnitRect unitRect: CGRect) -> UIImage? {
guard imageOrientation == .up, let cgImage = self.cgImage else { return nil }
let cgImageWidth = CGFloat(cgImage.width)
let cgImageHeight = CGFloat(cgImage.height)
let scaledRect = CGRect(x: unitRect.origin.x * cgImageWidth, y: unitRect.origin.y * cgImageHeight, width: unitRect.size.width * cgImageWidth, height: unitRect.size.height * cgImageHeight)
guard let croppedCgImage = cgImage.cropping(to: scaledRect) else { return nil }
return UIImage(cgImage: croppedCgImage, scale: scale, orientation: .up)
}
}
let image = #imageLiteral(resourceName: "image.png")
let celCount: CGFloat = 3
let cels = stride(from: 0, to: celCount, by: 1).map({ (i) -> UIImage in
image.subImage(inUnitRect: CGRect(x: i / celCount, y: 0, width: 1/3, height: 1))!
})
Then we can use a keyframe animation to crossfade the layer contents:
let imageView = UIImageView(image: cels[0])
PlaygroundPage.current.liveView = imageView
let animation = CAKeyframeAnimation(keyPath: "contents")
var values = [CGImage]()
var keyTimes = [Double]()
for (i, cel) in cels.enumerated() {
keyTimes.append(Double(i) / Double(cels.count))
values.append(cel.cgImage!)
// The 0.9 means 90% of the time will be spent *outside* of crossfade.
keyTimes.append((Double(i) + 0.9) / Double(cels.count))
values.append(cel.cgImage!)
}
values.append(cels[0].cgImage!)
keyTimes.append(1.0)
animation.keyTimes = keyTimes.map({ NSNumber(value: $0) })
animation.values = values
animation.repeatCount = .infinity
animation.duration = 5
imageView.layer.add(animation, forKey: animation.keyPath)
Result:
ORIGINAL
There are multiple ways you can do this. One is by setting or animating the contentsRect property of the image view's layer.
In your image, there are three cels, and each occupies exactly 1/3 of the image. The contentsRect is in the unit coordinate space, which makes computation easy. The contentsRect for cel i is CGRect(x: i/3, y: 0, width: 1/3, height: 0).
You want discrete jumps between cels, instead of smooth sliding transitions, so you need to use a keyframe animation with a kCAAnimationDiscrete calculationMode.
import UIKit
import PlaygroundSupport
let image = #imageLiteral(resourceName: "image.png")
let celSize = CGSize(width: image.size.width / 3, height: image.size.height)
let imageView = UIImageView()
imageView.frame = CGRect(origin: .zero, size: celSize)
imageView.image = image
PlaygroundPage.current.liveView = imageView
let animation = CAKeyframeAnimation(keyPath: "contentsRect")
animation.duration = 1.5
animation.calculationMode = kCAAnimationDiscrete
animation.repeatCount = .infinity
animation.values = [
CGRect(x: 0, y: 0, width: 1/3.0, height: 1),
CGRect(x: 1/3.0, y: 0, width: 1/3.0, height: 1),
CGRect(x: 2/3.0, y: 0, width: 1/3.0, height: 1)
] as [CGRect]
imageView.layer.add(animation, forKey: animation.keyPath!)
Result:
I have punch of images(5-6 images) that should animatedly scrolling on back ground with different speed. I put them just on UIView not in UIScrollView.
Right now I have this method for it:
I adding images on the screen that user see and copy of images behind the screen. But because of that images and copyImages(behind the screen) must pass the same distance them has different speed(Not that I put).
So it's not working properly for me.
Could anyone help with that?
P.S. Some images has different size so i need change yPos for them. Just for elaboration.
func setUpBackGroundLayersWithArray(){
var xPos: CGFloat = 0
for (index,image) in self.backGroundsImages.reverse().enumerate(){
var yPos:CGFloat = -30
switch index {
case 1: yPos = -10
xPos = 320
case 2: yPos = -10
default: yPos = -30
}
let imageView = UIImageView(image: image)
imageView.frame = CGRectMake(xPos, yPos, image.size.width, image.size.height)
imageView.contentMode = .ScaleAspectFit
self.addSubview(imageView)
let copyimageView = UIImageView(image: image)
copyimageView.frame = CGRectMake(320, yPos, image.size.width, image.size.height)
copyimageView.contentMode = .ScaleAspectFit
self.addSubview(copyimageView)
self.layoutIfNeeded()
let animator = UIViewPropertyAnimator(duration:8 - Double(index + 1), curve: .Linear, animations: {
UIView.animateKeyframesWithDuration(0, delay: 0, options: [.Repeat], animations: {
imageView.frame = CGRectMake(0 - copyimageView.frame.size.width, imageView.frame.origin.y, imageView.frame.size.width, imageView.frame.size.height)
}, completion: nil)
})
let secondAnimator = UIViewPropertyAnimator(duration:10 - Double(index + 1), curve: .Linear, animations: {
UIView.animateKeyframesWithDuration(0, delay: 0, options: [.Repeat], animations: {
copyimageView.frame = CGRectMake(0-copyimageView.frame.size.width, copyimageView.frame.origin.y, copyimageView.frame.size.width, copyimageView.frame.size.height)
}, completion: nil)
})
self.animators.append(animator)
self.animators.append(secondAnimator)
}
}
You can use UICollectionView for simple showing and animating on images. For animation you need use scrollToItemAtIndexPath:atScrollPosition:animated: method after needed time interval
I want to create resizable ui view background. It will resize depend on text. I tried following code:
let capInsetsIncoming = UIEdgeInsets(top: 17, left: 26.5, bottom: 17.5, right: 21)
self.contentView.backgroundColor = UIColor(patternImage: UIImage(named:"profileBioBackground")!.resizableImageWithCapInsets(capInsetsIncoming))
Result:
Expected:
This is my background image:
How can I fix this?
I think the easiest way is to use UIImageView as the background. And also, you have to split the original image so that that can be used as resizable image.
Anyway, here is the code:
class ViewController: UIViewController {
#IBOutlet var contentView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
contentView.backgroundColor = nil
let leftBackgroundView = UIImageView()
let rightBackgroundView = UIImageView()
let image = UIImage(named: "profileBioBackground")!
let scale = image.scale
let size = image.size
let leftRect = CGRect(
x: 0,
y: 0,
width: size.width / 2 * scale,
height: size.height * scale
)
let rightRect = CGRect(
x: size.width / 2 * scale,
y: 0,
width: size.width / 2 * scale,
height: size.height * scale
)
leftBackgroundView.image = UIImage(
CGImage: CGImageCreateWithImageInRect(image.CGImage, leftRect),
scale: scale,
orientation: .Up
)?.resizableImageWithCapInsets(UIEdgeInsets(top: 20, left: 4, bottom: 4, right: 16))
rightBackgroundView.image = UIImage(
CGImage: CGImageCreateWithImageInRect(image.CGImage, rightRect),
scale: scale,
orientation: .Up
)?.resizableImageWithCapInsets(UIEdgeInsets(top: 20, left: 16, bottom: 4, right: 4))
leftBackgroundView.setTranslatesAutoresizingMaskIntoConstraints(false)
rightBackgroundView.setTranslatesAutoresizingMaskIntoConstraints(false)
contentView.insertSubview(leftBackgroundView, atIndex: 0)
contentView.insertSubview(rightBackgroundView, atIndex: 0)
let views = ["left": leftBackgroundView, "right": rightBackgroundView]
contentView.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat("H:|[left][right(==left)]|", options: nil, metrics: nil, views: views)
+ NSLayoutConstraint.constraintsWithVisualFormat("V:|[left]|", options: nil, metrics: nil, views: views)
+ NSLayoutConstraint.constraintsWithVisualFormat("V:|[right]|", options: nil, metrics: nil, views: views)
)
}
}
And the Storyboard setup, and the result: