I am new in swift. Who can help? I have array with 3 links how can i take one link from this array and show in UIImageView then second link and then third? I make with one link but not understand how i can make with array. Thnx.
class ViewController: UIViewController {
#IBOutlet weak var imgView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
var image = UIImage()
var imageData: Data?
let links = ["http://s.ill.in.ua/i/gallery/950x0/2694/146192.jpg","http://s.ill.in.ua/i/gallery/950x0/2694/146190.jpg","http://s.ill.in.ua/i/gallery/950x0/2694/146202.jpg"]
let url = URL(string: links[0])
do {
imageData = try Data(contentsOf: url!)
}
catch {
print("error")
}
if let value = imageData {
image = UIImage(data:value)!
imgView.image = image
}
}
}
You can easily iterate through array like this:
for link in links {
// This will iterate through array, so you can do something specific on each step
let url = URL(string: link)
do {
imageData = try Data(contentsOf: url!)
}
catch {
print("error")
}
if let value = imageData {
image = UIImage(data:value)!
imgView.image = image
print("Updated imageView with \(link)!")
}
}
Just use this way :
let links = ["http://s.ill.in.ua/i/gallery/950x0/2694/146192.jpg","http://s.ill.in.ua/i/gallery/950x0/2694/146190.jpg","http://s.ill.in.ua/i/gallery/950x0/2694/146202.jpg"]
Take three different ImageView ( for a while ) or u can use tableview to show image and use following link : https://stackoverflow.com/a/37019507/3400991
for linkValue in links {
yourimageview.imageFromServerURL(urlString: linkValue as! String)
}
this is one of the way u can show image into your imageview.
Related
Goal is to get the image names from a directory and add them to an array of UIImages.
var photoArray = [UIImage]()
func getImageFromDocumentDirectory() -> [UIImage] {
let fileManager = FileManager.default
var imageNames = [String]()
let imagePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory,
.userDomainMask, true)[0] as NSString).appendingPathComponent("DIRECTORYNAME")
do {
let items = try fileManager.contentsOfDirectory(atPath: imagePath)
for item in items {
This is where I'm getting the problem: error: Found nil ( let images )
let images = UIImage(contentsOfFile: item)
photoArray.append(images!)
}
} catch {
print(error.localizedDescription)
}
return photoArray
}
Adding the func to a collection View to pull images.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)
-> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CELL",
for: indexPath) as! CELL
let images = getImageFromDocumentDirectory()
// photoImageView is a UIImageView in the cell.
cell.photoImageView.image = images[indexPath.row]
}
The problem is that – as you mentioned correctly – contentsOfDirectory(atPath returns an array of image names. To read the images from disk you need the full path.
I recommend to use the URL related API
func getImageFromDocumentDirectory() -> [UIImage] {
var images = [UIImage]()
let fileManager = FileManager.default
do {
let documentsDirectoryURL = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let folderURL = documentsDirectoryURL.appendingPathComponent("DIRECTORYNAME")
let urls = try fileManager.contentsOfDirectory(at: folderURL, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
for url in urls {
if let data = try? Data(contentsOf: url),
let image = UIImage(data: data) {
images.append(image)
}
}
} catch {
print(error.localizedDescription)
}
return images
}
I am trying to retrieve pictures saved from the database (in this case I hard coded the exact picture I want from the database). I have two UIImage variables initiated: imageView and rawImage. imageView is using SDWebImage from FirebaseUI to load the image, but I don't know how to save it into a UIImage array.
rawImage is grabbing the data from the path of the exact image from Firebase. I am converting it to a UIImage but when I want to append it to my UIImage array, the application crashes.
class GalleryViewController:UIViewController{
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var rawImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
view.addVerticalGradientLayer(topColor: primaryColor, bottomColor: secondaryColor)
self.tabBarController?.tabBar.isHidden = true
retrievePicture()
}
func retrievePicture()
{
guard let user_email = Auth.auth().currentUser?.email else { return }
let storageRef = Storage.storage().reference()
// Reference to an image file in Firebase Storage
let reference = storageRef.child("\(user_email)").child("/top").child("1.jpg")
// UIImageView in your ViewController
let imageView: UIImageView = self.imageView
// Placeholder image
let placeholderImage = UIImage(named: "placeholder.jpg")
//UploadViewController.Clothing.top_images.append(<#UIImage#>)
// Load the image using SDWebImage
imageView.sd_setImage(with: reference, placeholderImage: placeholderImage)
// this is the code for the rawImage portion I described above
reference.getData(maxSize: 1 * 1024 * 1024) { data, error in
if let error = error {
print("Error \(error)")
} else {
let picture = UIImage(data: data!)
UploadViewController.Clothing.top_images.append(picture!)
}
}
// loading the image to the rawImage imageview
rawImage.image = UploadViewController.Clothing.top_images[0]
}
}
If you already load the images using SDWebImage then you can also append the image in your UIImage array with that.
Just use SDWebImage with a completed block like this
var arr_image = [UIImage]()
imageView.sd_setImage(with: reference_url, placeholderImage: UIImage(named: "placeholder.jpg"), options: options: [.highPriority]) { (img, error, cachtype, url) in
if img != nil{
arr_image.append(img) // append UIimage in array
}
}
You have overlooked force unwrapping optionals, and I propose to you to remove ! usage to avoid crashes, use following instead
reference.getData(maxSize: 1 * 1024 * 1024) { data, error in
guard let data = data else {
print(error?.localizedDescription ?? "Check remote url or service")
return
}
if let picture = UIImage(data: data) {
UploadViewController.Clothing.top_images.append(picture)
} else {
print("Invalid image data\n\(String(data: data, encoding: .utf8))")
}
}
or following if statement is safe
if error == nil && data != nil, let picture = UIImage(data: data!) {
UploadViewController.Clothing.top_images.append(picture)
}
You can also try path without leading slash, like so:
let reference = storageRef.child("\(user_email)/top/1.jpg")
This is my code in swift 4 xcode. i have problems with my code. I can't show my array, with images on the simulator what things I'm doing wrong?
im a beginner in swift. and i have tried too look up how you write an array with images with four loop but the simulator docent show
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let img : UIImage = UIImage(named: "owl")!
let imageView = UIImageView(image: img )
self.view.addSubview(imageView)
// var images = Array<UIImage>()
//images.append(UIImage(named: "lion")!,
let ImgArray = ["lion.png","wolf.png","snake.png"]
var images = [UIImage]()
for i in 0..<ImgArray.count
{
images.append(UIImage(named: ImgArray[i])!)
}
let imageView2 = UIImageView(image: img )
self.view.addSubview(imageView2)
}
}
As explained by #Robert, instead of using the index of the array, you can use a for loop this way :
let imagesNames = ["img1.png", "img2.png", "img3.png"]
for imageName in imagesNames {
let image = UIImage(named: imageName)
images.append(image)
}
You can also use map to directly transform your array of images names to an array of images :
let imagesNames = ["img1.png", "img2.png", "img3.png"]
let images = imagesNames.map { UIImage(named: $0) }
where 0$ is your imageName.
I currently have a custom UICollection which loads a users video library from their camera roll. Now I am currently able to add all the videos into an array; and it prints out the correct count of videos; however my UICollection is not displaying all of my videos in my library (which amounts to 119). Anyone have any clue why this would be occurring?
Here is my code:
struct Media {
var image:UIImage?
var videoURL:NSURL?
}
var mediaArray = [Media]()
func grabPhotos(){
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
if let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions) {
if fetchResult.count > 0 {
for i in 0..<fetchResult.count{
var mediaItem = Media()
//Used for fetch Image//
imgManager.requestImage(for: fetchResult.object(at: i) as PHAsset , targetSize: CGSize(width: 400, height: 400), contentMode: .aspectFit, options: requestOptions, resultHandler: {
image, error in
let imageOfVideo = image! as UIImage
mediaItem.image = imageOfVideo;
//Used for fetch Video//
imgManager.requestAVAsset(forVideo: fetchResult.object(at: i) as PHAsset, options: PHVideoRequestOptions(), resultHandler: {(avAsset, audioMix, info) -> Void in
if let asset = avAsset as? AVURLAsset {
let videoData = NSURL(string: "\(asset.url)")
let duration : CMTime = asset.duration
let durationInSecond = CMTimeGetSeconds(duration)
print(durationInSecond)
mediaItem.videoURL = videoData!
self.mediaArray.append(mediaItem)
print(self.mediaArray.count)
}
})
})
}
}
else{
//showAllertToImportImage()//A function to show alert
}
}
}
And my cellForItemAt
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! VideoSelectionCVCell
cell.uploadedFile.image = mediaArray[indexPath.row].image
return cell
}
& Within my viewWillAppear I have the following creating the UICollection:
let flowLayout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: flowLayout)
collectionView.register(VideoSelectionCVCell.self, forCellWithReuseIdentifier: cellId)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.backgroundColor = UIColor.clear
self.view.addSubview(collectionView)
I think what is occurring is the screen is loading before the grabPhotos() occurs; and the grabPhotos() doesn't finish until the after the screen is loaded. I also have my UICollection being created in the viewWillAppear, so that would make it a private occurrence (if I'm correct). So I guess to fix this, I would need to make the UICollectionView public, but how would I do that if I am doing it programmatically + creating it in my View Will Appear?
There are a couple different ways you can solve this I think.
Move your remote image loading into cellforitemat
Add your collection view in Viewdidload, then at the end of the function call grabphotos.
In your grabphotos functions, call collectionView.reloadData() after you have the fetchResult.
Move imgManager.requestImage into cellforitem. This way you are only loading each image as the cells are rendered. The user isn't waiting for all the images to load before the collectionView is updated. You can add a prefetch if you are concerned about performance.
Use a DispatchGroup
If you really really want to load all the images before updating the collectionView, you can create a DispatchGroup to track the image downloads and then update the collectionView after it's all done.
struct Media {
var image:UIImage?
var videoURL:NSURL?
}
var mediaArray = [Media]()
let loadContentGroup = DispatchGroup()
func grabPhotos(){
loadContentGroup.notify(queue: DispatchQueue.main) { [weak self] in
guard let `self` = self else { return }
self.collectionView.reloadData()
}
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
if let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions) {
if fetchResult.count > 0 {
for i in 0..<fetchResult.count{
var mediaItem = Media()
loadContentGroup.enter()
//Used for fetch Image//
imgManager.requestImage(for: fetchResult.object(at: i) as PHAsset , targetSize: CGSize(width: 400, height: 400), contentMode: .aspectFit, options: requestOptions, resultHandler: {
image, error in
let imageOfVideo = image! as UIImage
mediaItem.image = imageOfVideo;
//Used for fetch Video//
imgManager.requestAVAsset(forVideo: fetchResult.object(at: i) as PHAsset, options: PHVideoRequestOptions(), resultHandler: {(avAsset, audioMix, info) -> Void in
if let asset = avAsset as? AVURLAsset {
let videoData = NSURL(string: "\(asset.url)")
let duration : CMTime = asset.duration
let durationInSecond = CMTimeGetSeconds(duration)
print(durationInSecond)
mediaItem.videoURL = videoData!
self.mediaArray.append(mediaItem)
print(self.mediaArray.count)
self.loadContentGroup.leave()
}
})
})
}
}
else{
//showAllertToImportImage()//A function to show alert
}
}
}
I have created a mini translation from English words to Spanish words. I would like to use the englishArray.plist instead of my englishArray = ["the cat"] How can I create this?
I have also used a localizable.strings to retrieve the value "the cat" for "el gato" but I would like to retrieve this from englishArray.plist
I started off with this but not sure if I'm on the right path
let path = NSBundle.mainBundle().pathForResource("englishArray", ofType: "plist")
let plistEnglishArray = NSArray(contentsOfFile: path!)
Here is the rest of my code:
var englishArray: [String] = ["rainbow", "boots", "heart", "leaf", "umbrella", "tired", "angry", "cry", "the cat" ]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.translateTextField.delegate = self
picker.delegate = self
picker.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func translateButtonTapped(sender: UIButton) {
let emptyString = self.translateTextField.text
if (emptyString!.isEmpty) {
print("please enter a word")
}
for transIndex in englishArray.indices {
if englishArray[transIndex] == emptyString!.lowercaseString {
//englishArray
//translateLabel.text = "\(spanishArray[transIndex])"
translateLabel.text = NSLocalizedString(emptyString!.lowercaseString, comment:"")
print(emptyString)
return
}
}
}
Swift 4
The absolute simplest way to do this is
let url = Bundle.main.url(forResource: "Sounds", withExtension: "plist")!
let soundsData = try! Data(contentsOf: url)
let myPlist = try! PropertyListSerialization.propertyList(from: soundsData, options: [], format: nil)
The object myPlist is an Array or Dictionary, whichever you used as the base of your plist.
Change your root object to Array, then
var myEnglishArray: [String] = []
if let URL = NSBundle.mainBundle().URLForResource("englishArray", withExtension: "plist") {
if let englishFromPlist = NSArray(contentsOfURL: URL) as? [String] {
myEnglishArray = englishFromPlist
}
}
Swift 4
You can use Codable which is pure swift type.
Firstly load Plist file from bundle then use PropertyListDecoder
Complete code -
func setData() {
// location of plist file
if let settingsURL = Bundle.main.path(forResource: "JsonPlist", ofType: "plist") {
do {
var settings: MySettings?
let data = try Data(contentsOf: URL(fileURLWithPath: settingsURL))
let decoder = PropertyListDecoder()
settings = try decoder.decode(MySettings.self, from: data)
print("array is \(settings?.englishArray ?? [""])")//prints array is ["Good morning", "Good afternoon"]
} catch {
print(error)
}
}
}
}
struct MySettings: Codable {
var englishArray: [String]?
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
englishArray = try values.decodeIfPresent([String].self, forKey: .englishArray)
}
}
This will read a resource in your bundle with the name "englishArray.plist" and store it in the immutable variable english. It will be an Optional that you should test before using.
It uses a closure to read the file and return the array, this lets you use a immutable value rather than a mutable variable that can be changed. It's a good idea to use immutables wherever you can - they promote stability.
import Foundation
let english:[String]? = {
guard let URL = NSBundle
.mainBundle()
.URLForResource("englishArray", withExtension: "plist") else {
return nil
}
return NSArray(contentsOfURL: URL) as? [String]
}()
Here is the solution for swift 3. For this solution you do not need to change types in your plist structure (keep Dictionary, Array, as is). Also note that since your array's name in plist is also englishArray so the (value for key) argument in the second if statement is also englishArray.
var myEnglishArray: [String] = []
if let URL = Bundle.main.url(forResource: "englishArray", withExtension: "plist") {
guard let englishFromPlist = NSDictionary(contentsOf: URL) else { return [] }
if let englishArray = englishFromPlist.value(forKey: "englishArray") as? [String] {
for myEnglish in englishArray {
myEnglishArray.append(myEnglish)
}
}
}