In my app I have a button. If you tap it you can download a couple of images (13) to your photo library.
I created an array that holds the images, named "1", "2", "3", "4", etc. like this: (I checked, the array holds all 13 images)
var myPhotos: NSMutableArray = []
for i in 1...13 {
let img = UIImage(named: "\(i)")!
myPhotos.addObject(img)
}
I then save all the images to the photo library:
var photo = UIImage()
var j = 0
for photo in myPhotos {
j++
UIImageWriteToSavedPhotosAlbum(photo as! UIImage, nil, nil, nil)
}
print(j)
print("done")
In the photo library of my simulator there are all the 13 images. When running this on my iPad that number varies a lot, from like 8 to 12 but never 13. That's why I put j there. It proves that the loop runs all the way through the array (j is 13 in the console) and "done" is also being printed.
What is the problem here?
Thanks in advance :)
this should work:
class ViewController: UIViewController {
var myPhotos = [UIImage]()
var savedPhotos = 0
override func viewDidLoad() {
super.viewDidLoad()
// setup photos
for i in 1...13 {
let img = UIImage(named: "\(i).jpg")! // remove the ".jpg" for you to work
myPhotos.append(img)
}
// save photos
saveNextPhotoToPhotosAlbum()
}
func saveNextPhotoToPhotosAlbum() {
if let photo = myPhotos.first {
UIImageWriteToSavedPhotosAlbum(photo, self, "image:didFinishSavingWithError:contextInfo:", nil)
}
}
func image(image: UIImage, didFinishSavingWithError error: NSErrorPointer, contextInfo:UnsafePointer<Void>) {
if error == nil {
print("saved \(++savedPhotos). photo")
myPhotos.removeFirst()
saveNextPhotoToPhotosAlbum()
}
}
}
in my case i used .jpg images so maybe you have to remove the extension in the for loop to make it work for you!
Related
I have 2 UIImageViews connected to an array of images
I'm trying compare both once they are displayed but doesn't seems to work.
I tried using imageArray[Image Literal] and also imageArray[image1.png, image2.png, image3.png, image4.png, image5.png]
I'm not sure what im doing wrong.
im not looking for the code although it may help but what im looking is for a someone to guide me to the right direction
#IBOutlet weak var 1ImageView: UIImageView!
#IBOutlet weak var 2ImageView: UIImageView!
let imageArray = [image1.png, image2.png, image3.png, image4.png, image5.png]
func any() {
if (1ImageView != nil) && (2ImageView != nil) && isEqual(image1.png) {
print("match!")
} else if ...// more if statements
…last if statement} else {
print(“no match!”)
}
#IBAction func buttonPressed(_ sender: IUButton) {
any()
}
If this is not possible is there a way to assign an identifier to each of the images inside the array..
sorry for the extra question.
there is one answer on comparing 2 images using NSData but Im not sure how to implement it to an array.
thanks and sorry but the newbie question.
image.isEqual(image) seems to be unreliable, despite what documentation says. If you don't need to make a pixel perfect comparison, converting image to a data and comparing those would be sufficient.
let image1 = UIImage(named: "image1.png")
let image2 = UIImage(named: "image2.png")
let imageData1 = image1?.pngData()
let imageData2 = image2?.pngData()
if imageData1 == imageData2 {
print("images are the same")
} else {
print("images are different")
}
Looking for a specific image inside an array can build on that:
// array of images referencing image files within an Xcode project
// it's not the best idea to force unwrap those, but for the sake of simplicity
let imageArray = [UIImage(named: "image1.png")!,
UIImage(named: "image2.png")!,
UIImage(named: "image3.png")!,
UIImage(named: "image4.png")!,
UIImage(named: "image5.png")!]
func anySimilarImages() {
// find first image which is the same as 1ImageView's
let 1ImageViewImage: UIImage? = imageArray.first { (image) -> Bool in
return image.pngData() == 1ImageView.image?.pngData()
}
// find first image which is the same as 2ImageView's
let 1ImageViewImage: UIImage? = imageArray.first { (image) -> Bool in
return image.pngData() == 2ImageView.image?.pngData()
}
if 1ImageViewImage != nil && 2ImageViewImage != nil {
print("both images were found")
}
else if 1ImageViewImage != nil {
print("first image was found")
}
else if 2ImageViewImage != nil {
print("second image was found")
}
else {
print("no image was found")
}
}
Hi I'm a newbie developer from Taiwan.
I have started to use firebase(ios) storage for my app and I want to use ImageSliderShow to make a image slider view. After I got the image URL from firebase. I appended the url string into array then I ran the app. The image slider view works fine but the new image just showing nothing. Then I printed the url String before and after I appended into the array. It gave me two different string. Here is my code.
override func viewDidLoad() {
super.viewDidLoad()
let URLString = loadURL()
URLString.getURL(){
(result:String) in
self.ImageSliderView.backgroundColor = UIColor.white
self.ImageSliderView.slideshowInterval = 5.0
self.ImageSliderView.pageControlPosition = PageControlPosition.insideScrollView
self.ImageSliderView.pageControl.currentPageIndicatorTintColor = UIColor.lightGray
self.ImageSliderView.pageControl.pageIndicatorTintColor = UIColor.black
self.ImageSliderView.contentScaleMode = UIViewContentMode.scaleAspectFill
self.ImageSliderView.activityIndicator = DefaultActivityIndicator()
self.ImageSliderView.currentPageChanged = { page in
print("current page:", page)
}
print("Result: \(result)") ////print the result string before appended into array.
self.kingfisherSource.append(KingfisherSource(urlString: String(result))!)
print("alamoArray3: \(self.kingfisherSource[3].url)") ////print the result string after appended into array.
self.ImageSliderView.setImageInputs(self.kingfisherSource)
let recognizer = UITapGestureRecognizer(target: self, action: #selector(ResultViewController.didTap))
self.ImageSliderView.addGestureRecognizer(recognizer)
}
}
func didTap() {
let fullScreenController = ImageSliderView.presentFullScreenController(from: self)
// set the activity indicator for full screen controller (skipping the line will show no activity indicator)
fullScreenController.slideshow.activityIndicator = DefaultActivityIndicator(style: .white, color: nil)
}
getURL function:
class loadURL {
let storage = Storage.storage(url: "my-firebase-storage-bucket")
func getURL(completion:#escaping (_ result:String)->Void) {
self.storage.reference().child("images/breakfast/1/1.jpg").downloadURL { url, error in
if error != nil {
print("Firebase Image URL error: \(String(describing: error))")
} else {
print("Firebase Image URL: \(String(describing: url!))")
completion("\(String(describing: url))")
}
}
}
The output:
Firebase Image URL: https://firebasestorage.googleapis.com/v0/b/my-firebase-storage-bucket/o/images%2Fbreakfast%2F1%2F1.jpg?alt=media&token=15ee8094-ac50-4e93-adc0-200793181bfc
Result: Optional(https://firebasestorage.googleapis.com/v0/b/my-firebase-storage-bucket/o/images%2Fbreakfast%2F1%2F1.jpg?alt=media&token=15ee8094-ac50-4e93-adc0-200793181bfc)
alamoArray3: Optional(https://firebasestorage.googleapis.com/v0/b/my-firebase-storage-bucket/o/images%2Fbreakfast%2F1%2F1.jpg?alt=m ... 3181bfc)
The 3rd output just weird... Is it possible be an encoding issue?
And I apology about my bad English.
Finally I just figure it out. I let the type of result as URL and then it just works perfectly.
So I have an array of images I've accessed from my xcassets for demonstration purposes. There are 150 images I'm trying to save to my parse server at one time using parse frameworks. Here is the code I have so far. The problem I have is my app cpu goes to 100% in the tests and drops to 0. Also the images aren't saving to parse. I was hoping someone could help me find an efficient way to save 150 images to parse.
var imageNameList: [String] {
var imageNameList2:[String] = [] //[NSMutableArray]()
for i in 0...149 {
let imageName = String(format: "pic_%03d", Int(i))
imageNameList2.append(imageName)
}
return imageNameList2
}
#IBAction func Continue(_ sender: Any) {
for imageName in imageNameList {
var objectForSave:PFObject = PFObject(className: "Clo")
let object:UIImage = UIImage(named: imageName)!
let tilesPF = imageNameList.map({ name in
let data = UIImagePNGRepresentation(object as! UIImage)!
let file = PFFile(data: data)
let tile = PFObject(className: "Tile")
tile["tile"] = file
})
objectForSave["tiles"] = tilesPF
objectForSave.saveInBackground(block: { responseObject, error in
//you'll want to save the object ID of the PFObject if you want to retrieve a specific image later
})
}
}
The trouble is that the tight for-loop launches all of those requests concurrently causing some part of the http stack to bottleneck.
Instead, run the requests sequentially as follows (in my best approximation of Swift)...
func doOne(imageName: String, completion: (success: Bool)->()) {
var objectForSave:PFObject = PFObject(className: "Clo")
let object:UIImage = UIImage(named: imageName)!
// ... OP code that forms the request
objectForSave.saveInBackground(block: { responseObject, error in
success(error == nil)
})
}
func doMany(imageNames: Array<String>, completion: (success: Bool)->()) {
if (imageNames.count == 0) return completion(YES)
let nextName = imageNames[0];
self.doOne(imageName:imageNames[0] completion: {(success: Bool) -> Void in
if (success) {
let remainingNames = imageNames[1..imageNames.count-1]
self.doMany(imageNames: remainingNames completion:completion)
} else {
completion(NO)
})
}
In English, just in case I goofed the Swift, the idea is to factor out a single request into it's own function with a completion handler. Build a second function that takes an array of arguments to the network request, and use that array like a to-do list: do the first item on the list, when it completes, call itself recursively to do the remaining items.
I'm using this library in my app for banners. I am trying to get the Link by parsing the JSON.
The Images are are not showing in the slideshow view. If I press the slideshow view, after that everything works fine. I thought that there was some issue with my completion handler.
But I can't solve it yet :)
#IBOutlet weak var slideshow: ImageSlideshow!
var transitionDelegate: ZoomAnimatedTransitioningDelegate?
var Banner : [AlamofireSource] = []
override func viewDidLoad() {
super.viewDidLoad()
Banners { (imagesource) in
if imagesource != nil {
self.bannershow()
}
}
}
func Banners(completionHandler: ([AlamofireSource]?) -> ()) -> (){
Alamofire.request(.GET, "http://46.420.116.11/mobileapp/gps/api.php?rquest=get_banners")
.responseJSON{ response in
if let data = response.result.value{
let json = JSON(data)
let count = json["image_path"].count
for index in 0...count-1 {
let image :String = json["image_path"][index].stringValue
let source : AlamofireSource = AlamofireSource(urlString: image)!
self.Banner.append(source)
}
completionHandler(self.Banner)
}
}
}
func bannershow(){
self.slideshow.backgroundColor = UIColor.whiteColor()
self.slideshow.slideshowInterval = 2.0
self.slideshow.contentScaleMode = UIViewContentMode.ScaleToFill
self.slideshow.setImageInputs(self.Banner)
let recognizer = UITapGestureRecognizer(target: self, action: "click")
self.slideshow.addGestureRecognizer(recognizer)
}
func click() {
let ctr = FullScreenSlideshowViewController()
ctr.pageSelected = {(page: Int) in
self.slideshow.setScrollViewPage(page, animated: false)
}
ctr.initialPage = slideshow.scrollViewPage
ctr.inputs = slideshow.images
self.transitionDelegate = ZoomAnimatedTransitioningDelegate(slideshowView: slideshow);
ctr.transitioningDelegate = self.transitionDelegate!
self.presentViewController(ctr, animated: true, completion: nil)
}
You probably have a threading problem. There is no guarantee that the Banners completion handler is called on the main thread. You need to step out to the main thread explicitly before doing anything that touches your properties or (especially) the interface.
I think your problem might be that you're expecting the images to be available immediately but they need to be downloaded before, so they won't be available immediately after your viewDidLoad method finished. That's why you should probably configure your slideshow in the viewDidLoad and not in your bannershow() method. Something like this might be an improvement:
#IBOutlet weak var slideshow: ImageSlideshow!
var bannerImages : [AlamofireSource] = []
override func viewDidLoad() {
super.viewDidLoad()
slideshow.backgroundColor = UIColor.whiteColor()
slideshow.slideshowInterval = 2.0
slideshow.contentScaleMode = UIViewContentMode.ScaleToFill
let recognizer = UITapGestureRecognizer(target: self, action: "click")
slideshow.addGestureRecognizer(recognizer)
getBanners { imagesource in
self.showBanner()
}
}
func getBanners(completionHandler: ([AlamofireSource]?) -> ()) -> (){
Alamofire.request(.GET, "http://46.420.116.11/mobileapp/gps/api.php?rquest=get_banners")
.responseJSON{ response in
if let data = response.result.value{
let json = JSON(data)
let count = json["image_path"].count
for index in 0...count-1 {
let image :String = json["image_path"][index].stringValue
let source : AlamofireSource = AlamofireSource(urlString: image)!
self.bannerImages.append(source)
}
}
completionHandler(self.bannerImages)
}
}
func showBanner() {
slideshow.setImageInputs(bannerImages)
}
Move the code to viewWillAppear.
override func viewWillAppear(animated: Bool) {
Banners { (imagesource) in
if imagesource != nil {
self.bannershow()
}
}
}
func Banners(completionHandler: ([AlamofireSource]?) -> ()) -> (){
Alamofire.request(.GET, "http://46.420.116.11/mobileapp/gps/api.php?rquest=get_banners")
.responseJSON{ response in
if let data = response.result.value{
let json = JSON(data)
let count = json["image_path"].count
for index in 0...count-1 {
let image :String = json["image_path"][index].stringValue
let source : AlamofireSource = AlamofireSource(urlString: image)!
self.Banner.append(source)
}
completionHandler(self.Banner)
}
}
}
You are executing for loop in Banners fun
for index in 0...count-1 {
let image :String = json["image_path"][index].stringValue
let source : AlamofireSource = AlamofireSource(urlString: image)!
self.Banner.append(source)
}
Replace this code in some other method and place an Optional
var image :String? = json["image_path"][index].stringValue
or place an thumb image, That will make you confirm that image is downloaded successfully or not .
Let me know if it works
Thanks, Happy Coding
Maybe you don't see images because you update them in a secondary thread, you have to perform image update in main thread;
In swift ( performSelectorOnMainThread() is not available), you may use something like this :
dispatch_async(dispatch_get_main_queue(), {
myslideshow.updateimage();
})
Not really sure, but I am guessing that since your images need to get downloaded, they are nil when you call self.slideshow.setImageInputs(self.Banner), which internally sets the image from the list to an imageview which is added inside the scrollView. So one way I can think of is use SDWebImage to set the image to the imageView so that it updates the respective imageViews once the image is ready(downloaded). I think you will need to use it in the InputSource.swift class in the setToImageView(_:) method. You will have to check this though, this is the only possible problem i could think of that might be causing your issue.
I have a map that allows me to add pins (annotations) to it. When I click on a pin, the app goes to another view controller and shows images downloaded from online. I "save" these images to an array but later I cannot access them. For example, when I tap "Back" to go to the previous view controller and tap on the same pin from before, instead of showing the images I originally downloaded and saved, new images are downloaded from online. Essentially, the images in the array are replaced. How can I save the images and retrieve them? Sorry for long amounts of code, I shortened as much as I could.
This is my class for the images:
class Image {
var image: UIImage
init(image: UIImage) {
self.image = image
}
}
This is my class for the pins. Notice, an array of type Image from the Image class is in here:
class Pin: Hashable {
var hashValue: Int {
get {
return "\(latitude.hashValue),\(longitude.hashValue)".hashValue
}
}
let latitude: Double
let longitude: Double
var images = Array<Image>()
init(latitude: Double, longitude: Double)
{
self.latitude = latitude
self.longitude = longitude
}
}
// must be declared in the global scope! and not just in the class scope
func ==(lhs: Pin, rhs: Pin) -> Bool
{
return lhs.hashValue == rhs.hashValue
}
I add pins to a Set like so (no duplicates of pins allowed). Also, the selected pin is sent to the secondViewController:
class MapViewController: UIViewController, MKMapViewDelegate, UIGestureRecognizerDelegate {
var pins = Set<Pin>()
func mapView(mapView: MKMapView!, didSelectAnnotationView view: MKAnnotationView!) {
// Add pin to set
let selectedCoordinatePoint = Pin(latitude: latFromPin, longitude: lonFromPin)
var select = "\(latFromPin.hashValue),\(lonFromPin.hashValue)".hashValue
pins.insert(selectedCoordinatePoint)
//Goto to next view controller and show data depending on the pin selected
for pin in pins {
if pin.hashValue == select.hashValue {
dispatch_async(dispatch_get_main_queue()) {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let secondViewController = self.storyboard!.instantiateViewControllerWithIdentifier("CollectionViewControllerID") as! CollectionViewController
secondViewController.pin = pin
self.navigationController?.pushViewController(secondViewController, animated: true)
}
}
}
}
}
I download images from online and append to an array on this secondViewController (I shortened the code here):
class CollectionViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, MKMapViewDelegate {
var pin: Pin!
override func viewWillAppear(animated: Bool) {
if let photosArray = photosDictionary["photo"] as? [[String:AnyObject]] {
// println("photosArray = \(photosArray )")
var count = 0
for photo in photosArray {
// 6 - Grab 21 random images
if count <= 20 {
// Grabs 1 image
let randomPhotoIndex = Int(arc4random_uniform(UInt32(photosArray.count)))
let photoDictionary = photosArray[randomPhotoIndex] as [String:AnyObject]
// 7 - Get the image url
let imageUrlString = photoDictionary["url_m"] as? String
let imageURL = NSURL(string: imageUrlString!)
// 8 - If an image exists at the url, append to array
let imageData = NSData(contentsOfURL: imageURL!)
let finalImage = UIImage(data: imageData!)
var image = Image(image: finalImage!)
self.pin.images.append(image)
count += 1
println(self.pin.images.count)
}
}
}
}
}
I'm not I understand what your asking exactly...you are making network calls to populate your image array. If you just want to pass the image array from VC to VC, you can use prepareForSegue to pass the value.
If you want to pull these down and then store them locally you need to read up on persistence. For example you can use something like Core Data, Parse, Realm...I personally use Parse because of it's ease. This is a good tutorial along the lines of what you're doing I think. It uses Parse to store the image data: http://www.appcoda.com/instagram-app-parse-swift/