I am using UIImageWriteToSavedPhotosAlbum() to save images to the photo album of the device.
I have my images in an array and they are displayed on a PageControl.
A download icon on the navigationBar saves the image to Photos.
I am able to save the image to Photos but the image saved is incorrect i.e. the image at the particular index is not saved. This is how I am adding all images to the page-control....
func scrollImagesWhileViewChange() {
for i in arrayOfURLImages {
self.pageControl.size(forNumberOfPages: arrayOfURLImages.count)
let scrollViewWidth:CGFloat = self.scrollView.frame.width
let scrollViewHeight:CGFloat = self.scrollView.frame.height
let index = arrayOfURLImages.index(of: i)
let imageA = UIImageView(frame: CGRect(x: CGFloat(index!) * scrollViewWidth, y:0,width:scrollViewWidth, height:scrollViewHeight))
imageA.image = i
self.scrollView.addSubview(imageA)
self.imageToDownload = imageA
self.scrollView.contentSize = CGSize(width:self.scrollView.frame.width * CGFloat(arrayOfURLImages.count), height:self.scrollView.frame.height)
self.scrollView.delegate = self
self.pageControl.numberOfPages = arrayOfURLImages.count
self.pageControl.currentPage = 0
}
}
In the section self.imageToDownload = imageA , I am assigning the image to a UIImageView variable. And this is how I am saving to 'Photos'..
UIImageWriteToSavedPhotosAlbum(self.imageToDownload.image!, self, #selector(MyViewController.image(_:didFinishSavingWithError:contextInfo:)), nil)
Here the first parameter should get the correct index. But I am not able to figure out how...
When you click download button you need to figure out the current index of page control and then on basis of current index fetch right image from arrayOfURLImages and then save to photo album.
Try this-
UIImageWriteToSavedPhotosAlbum(arrayOfURLImages[self.pageControl.currentPage], self, #selector(MyViewController.image(_:didFinishSavingWithError:contextInfo:)), nil)
Related
I have an image slider where multiple images are added to a scrollview. Each is stored in an array of my custom data type (named Slide). I cycle through them like this:
for (index, slide) in slides.enumerated() {
let imageView = UIImageView()
imageView.image = UIImage(named: slide.image) //set the imageView with the imageName in this slide
imageView.contentMode = .scaleAspectFit
let xPos = self.view.frame.width * CGFloat(index) //calculate x depending upon slide index
imageView.frame = CGRect(x: xPos, y: -200, width: self.SVSlider.frame.width, height: self.SVSlider.frame.height)
SVSlider.contentSize.width = SVSlider.frame.width * CGFloat(index + 1) //add slide image to scrollview
SVSlider.addSubview(imageView)
//add tap function to slide
imageView.isUserInteractionEnabled = true
imageView.tag = index
let tapImage = UITapGestureRecognizer(target: self, action: #selector(slideAction(_:)))
imageView.addGestureRecognizer(tapImage)
}
Each slide image is attached to the method "slideAction", below:
#objc func slideAction(_ sender: UITapGestureRecognizer) {
let selName = slides[(sender.view?.tag)!].target
NSObject.perform(Selector(selName))
}
But the application throws an error when I click an image. By debugging I can see that the "tag" is correctly set to the slide index number. I want to perform the target action held in the slide object. (Currently the target is a String of the method name - ending with ':').
How can I get the "slideAction" to goto the method in the slide object?
Try this ,create target method in the custom class callMethodDirectly and call it
#objc func slideAction(_ sender: UITapGestureRecognizer) {
let selName = slides[(sender.view?.tag)!]
selName.callMethodDirectly()
}
class slide:NSObjcet
{
func callMethodDirectly()
{
// implement target code here
}
}
I have a view controller like this:
There is an ImageView and a RangeSlider.
The behavior should be this:
If the user slides the range slider, the image in the ImageView should switch to a new image inside an array of images. The user also can do zooming operations on the image without losing the quality of the pictures.
So what I did was to download the images into the image array on viewDidLoad:
//image array
var images = [UIImage]()
I'm downloading 48 images with the size of 1920 × 1920 px and about 120 KB per image and creating UIImage objects of the downloaded data and saving them into the array like this:
DispatchQueue.global().async { [weak self] in
let data = try? Data(contentsOf: url)
DispatchQueue.main.async {
if data != nil {
let img = UIImage(data: data!)
self?.images.append(img!)
} else {
...
}
}
}
To change the image inside the imageView I created this function:
func updateImage(currentImage: UIImage) {
self.imageView.image = currentImage
}
When the user starts to slide the rangeSlider the memory usage of the app runs from 40 MB to 700 MB and stops increasing after the user switched all the images through. After that point the user can switch the whole images back and forward without any effect on the memory usage.
But when I do a little resizing before updating the image, the memory issue gets solved but the images are now low quality if the user zooms a bit.
Resizing part:
extension UIImage {
func resized(_ newSize:CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
self.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
func updateImage(currentImage: UIImage) {
let currentImg = currentImage.resized(self.imageView.bounds.size)
self.imageView.image = currentImg
}
And for zooming I use this:
#IBAction func scaleImage(_ sender: UIPinchGestureRecognizer) {
print(self.imageView.transform)
let tmp = self.imageView.transform.scaledBy(x: sender.scale, y: sender.scale)
if (tmp.a >= 0.99 || tmp.d >= 0.99) && (tmp.a < 2.5 || tmp.d < 2.5) {
self.imageView.transform = self.imageView.transform.scaledBy(x: sender.scale, y: sender.scale)
sender.scale = 1
if (tmp.a <= 1.2 || tmp.d <= 1.2) {
self.imageView.center = defaultImageCenter
}
}
}
So how can I achieve it in a elegant way to provide no memory leaks but still use the best image quality?
if data != nil {
let img = UIImage(data: data!)
self?.images.append(img!)
}
At this part, instead of creating a UIImage and adding it to an array, just save the data to the disk and store its location an array. Then when you move the slider, create the UIImage from the location matching the slider's position. This way there will be only one UIImage loaded in the memory at a time.
If you want to make it faster, load also the previous and next image (for example you are at position 4, load images 3 and 5 too, then when you move the slider to position 5, you will already have the image - at this point release image 3 and load 6). This will have a larger memory footprint, but when you move the slider you will see the images immediately.
You might also want to not download all the images at once to make this faster, but that's not related to memory, so this should be enough for your problem.
I currently have an image set into my UIImageView the following way:
art_image.image = UIImage(named:(artworkPin.title!))
where art_image is the image View and artworkPin.title refers to the name of the image. However, I want to add a second image to the UIImageView if it exists I thought of programming it as
art_image.image = UIImage(named:(artworkPin.title + "1")?)
would this work? In this case I would name a second image the name of the first image but with a '1' on the end. Example: 'Photo.jpeg' and 'Photo1.jpeg' would both be in the image view if Photo1.jpeg existed.
Thanks for your help.
I came across a similar task myself once. What I did was, I created a UIScrollView with the frame of the UIImageView and its contentSize would be imageView.frame.size.width * numberOfImages.
let scrollView = UIScrollView(frame: view.bounds)
view.addSubview(scrollView)
scrollView.contentSize = CGSize(width: scrollView.bounds.size.width * CGFloat(numberOfImages), height: scrollView.bounds.size.height))
for i in 0...<numberOfImages.count-1 {
let imageView = UIImageView(frame: CGRect(x: scrollView.bounds.size.width * CGFloat(i), y: scrollView.bounds.origin.y, width: scrollView.bounds.size.width, height: scrollView.bounds.size.height))
imageView.image = UIImage(named: "artwork" + "\(i)")
scrollView.addSubview(imageView)
}
You can animate it to scroll with a Timer if you want.
scrollView.setContentOffset(scrollPoint, animated: true)
you can only show 1 image inside the imageivew at a time, so if you have the lines as follows:
art_image.image = UIImage(named:(artworkPin.title!))
art_image.image = UIImage(named:(artworkPin.title! + "1")?)
art_image would consist only of Photo1 provided such an image exists and that the artworkPin.title unwrapped is also not nil otherwise you could see some different results.
if you do want to add multiple images to an image view for the purpose of animation, you need to use the animationImages property of UIImageView which takes an array of UIImages for example
art_image.animationImages = [UIImage.init(named:"Photo")!,
UIImage.init(named:"Photo1")!]
Hope this helps
EDIT
var imagesListArray = [UIImage]()
for position in 1...5
{
if let image = UIImage.init(named: ("Photo\(position)"))
{
imagesListArray.append(image)
}
}
art_image.animationImages = imagesListArray
art_image.animationDuration = 3.0
art_image.startAnimating()
This would be a safer a way to add the images so it will ONLY add an image if it is not nil and adds Photo1, Photo2 ..... Photo5
With regards to your other questions:
If you want the user to be able to
What do you mean by animation?
I have added two more lines of code, and it gives this result:
art_image.animationDuration = 1.0
art_image.startAnimating()
It will give you something like this:
If you want the user to swipe, then you need to make some changes such as:
using a scrollview, collectionview for example is the easiest or using a gesture recognizer on swipe you need to change the image
EDIT
Have a look at this example. Imagine each button is your annotation so when I tap it, the image changes.
I have 3 images named Photo11.png, Photo21.png and Photo31.png and this is my code inside the button handler
#IBAction func buttonTapped(_ sender: UIButton)
{
if let image = UIImage.init(named: sender.currentTitle!+"1")
{
art_image.image = image
}
}
As you can see I am setting the image with the title of my button + "1" as so it displays either Photo11.png or Photo21.png etc
So Ive got a Feed displaying posts made by users (Using Parse). At present the cell contains a title, profile image of user who posted, the date, content(text), an imageView (for an image they post) and a like & comment button.
My question is, Using autolayout what is the best approach to displaying the image they posted. Currently I have the imageView height set to 0 at all times, I then reference the constant height of the imageView in the code.
The app pulls down all the posts and starts loading the tableView of posts.
The code for loading an image in cell is
if let imageToFetch = post.objectForKey("postImage") as? PFObject {
self.postImageHeight.constant = 500
if let fetchImage = imageToFetch.objectForKey("image") as? PFFile{
fetchImage.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
self.postImage.image = image
self.imageGesture(self.postImage)
}
}
}
} else {
//Make the size constarints of the image 0
self.postImageHeight.constant = 0
}
But it seems like a unstable approach as I'm hardcoding the image height. Any help on the issue would be appriciated.
You can change them using the image's size property. But for this, you need to use a fixed width referenced here as kImageWidth
self.postImageHeight.constant = kImageWidth * image.size.height / image.size.width
self.layoutIfNeeded()
Simple answer: calculate postImageHeight manually after setting postImage.image:
/// .....
self.postImage.image = image
self.postImageHeight.constant = self.postImage.bounds.size.width * image.size.height / image.size.width
/// .....
A better option is to add a special method to UITableViewCell subclass:
func setPostImageWithImage(image: UIImage?) {
postImageView.image = image
// Manually adjust image height.
if let image = image {
postImageViewHeightConstraint.constant = postImageView.bounds.size.width * image.size.height / image.size.width
}
else {
// Collapse if there is no image.
postImageViewHeightConstraint.constant = 0
}
}
I am writing a small app that takes a photo and do modifications to it. There is a feature to put stickers(images) on top of the taken photo. I want the user to be abel to pinch rotate and drag the stickers so I used a UIImageView to contain the image so that I could use gesture functions to modify it.
But here is the problem, after the user finished modifying the stickers, how could I save the photo with the sticker? They are in different views and the only thing I can think of is to keep track of the modifications to the stickers and draw them on the photo after the user finished modifying it. Is there a easier way? What should I do?
func addSticker(name: String)
{
var stickerModView = UIImageView(frame: CGRect(blah blah))
var sticker = UIImage(named:"blah blah.png")
stickerModView.image = sticker
self.view.addSubview(stickerMod)
var tapRec = UITapGestureRecognizer()
var pinchRec = UIPinchGestureRecognizer()
var rotateRec = UIRotationGestureRecognizer()
var panRec = UIPanGestureRecognizer()
pinchRec.addTarget(self, action: Selector("pinchedView:"))
rotateRec.addTarget(self, action: Selector("rotatedView:"))
panRec.addTarget(self, action: Selector("draggedView:"))
tapRec.addTarget(self, action: Selector("tappedView:"))
stickerModView.addGestureRecognizer(pinchRec)
stickerModView.addGestureRecognizer(rotateRec)
stickerModView.addGestureRecognizer(panRec)
stickerModView.userInteractionEnabled = true
stickerModView.multipleTouchEnabled = true
}
After adding your complete UIImageView with editing, you can try this,
let rect : CGRect = CGRect() //Your view size from where you want to make UIImage
UIGraphicsBeginImageContext(rect.size);
let context : CGContextRef = UIGraphicsGetCurrentContext()
self.view.layer.renderInContext(context)
let img : UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
//your image ready to save in img
It may help you!!