I have a UImageview with animated image.
i am adding the uiimageview in code and its a part of a CollectionViewCell
When the user touches the cell the animation stops, why does this happen?
code:
var images: [UIImage] = []
for i in 0...10 {
images.append(UIImage(named: "image\(i)"))
}
let i = UIImageView(frame: CGRect(x: xPos, y: yPos, width: 200, height: 200))
i.animationImages = images
i.animationDuration = 0.5
i.startAnimating()
i.contentMode = UIViewContentMode.Center
i.userInteractionEnabled = false
self.addSubview(i)
If you don't want any interaction then following will be the fastest way to resolve this issue:
collectionView.allowsSelection = false
In your custom collection view cell class, write following methods to fix issue
func setSelected(selected:Bool) {
}
func setHighlighted(higlighted:Bool) {
}
Swift 4.0 Version:
override open var isSelected: Bool
{
set {
}
get {
return super.isSelected
}
}
override open var isHighlighted: Bool
{
set {
}
get {
return super.isHighlighted
}
}
Overriding isSelected, isHighlighted with empty setter will solve this issue, but it will lose those two properties to be set. I was able to solve this issue by calling imageView.startAnimating() at didSelectItemAt in UICollectionViewDelegate.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let item = items[indexPath.item]
if item.hasGIF {
let cell = collectionView.cellForItem(at: indexPath) as! ItemCell
cell.imageView.startAnimating()
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let item = items[indexPath.item]
if item.hasGIF {
let cell = collectionView.cellForItem(at: indexPath) as! ItemCell
cell.imageView.startAnimating()
}
}
In my case, I can't disable selection or use any of the solutions posted before here. I do not need to highlight the cell so I disabled it through the delegate method below to return false which prevented the stopAnimating() method from being called. This was an issue I encountered when using the AnimatedImageView of KingFisher used in a UICollectionViewCell.
func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
return false
}
An array of UIImage objects to use for an animation. var highlightedAnimationImages: [UIImage]? An array of UIImage objects to use for an animation when the view is highlighted. The amount of time it takes to go through one cycle of the images.
I created a custom UIImageView class and only stopped animation if I want it to stop.
class GifImageView: UIImageView {
private var stop = false
static func getAudioGif(_ size: CGFloat) -> GifImageView {
return fromGif(frame: CGRect(x: 0, y: 0, width: size, height: size), resourceName: "audio")!
}
override func stopAnimating() {
if stop {
stop = false
super.stopAnimating()
}
}
func stopForReal() {
stop = true
stopAnimating()
}
func fromGif(frame: CGRect, resourceName: String) -> GifImageView? {
guard let path = Bundle.main.path(forResource: resourceName, ofType: "gif") else {
print("Gif does not exist at that path")
return nil
}
let url = URL(fileURLWithPath: path)
guard let gifData = try? Data(contentsOf: url),
let source = CGImageSourceCreateWithData(gifData as CFData, nil) else { return nil }
var images = [UIImage]()
let imageCount = CGImageSourceGetCount(source)
for i in 0 ..< imageCount {
if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
images.append(UIImage(cgImage: image))
}
}
let gifImageView = GifImageView(frame: CGRect(x: 0, y: 0, width: frame.width, height: frame.height))
gifImageView.animationImages = images
return gifImageView
}
}
In TableView Use code below can solve touche cancel, touche moved and so on
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath {
[cell startAnimation];
}
Related
I am using collection view with pagination to show more data. At end of collection view I show an image view with gif file until data loads and then hide the image view. But sometimes gif doesn't load in the image view.
Please help and thanks in advance !!
I have tried changing libraries - Kingfisher, SDWebImage, ImageIO, UIImage+Gif etc. but it didn't help.
Also tried running on main thread.
class HorizontalScrollDealCollectionView: UICollectionView {
private var indicator:UIImageView!
private var offset:CGFloat = 0
private let loaderGif = UIImage.gif(name: "831")
override func awakeFromNib() {
super.awakeFromNib()
let frame = CGRect(x: 20, y: self.frame.height/2-15, width: 30, height: 30)
indicator = UIImageView(frame: frame)
indicator.image = loaderGif
indicator.backgroundColor = .blue
self.addSubview(indicator)
indicator.isHidden = true
}
override func layoutSubviews() {
super.layoutSubviews()
self.adjustIndicator()
}
func loadGifAnimatedIndicator() {
indicator.isHidden = true
indicator.image = loaderGif
}
private func adjustIndicator() {
indicator.frame.origin.x = self.contentSize.width + (offset/2)
let view = self.visibleCells.first
if let scrollViewAdjuster = view as? InfiniteScrollOffsetAdjuster {
indicator.frame.origin.y = scrollViewAdjuster.refralFrameForProgressView.height / 2 - 15
}
else {
indicator.frame.origin.y = self.contentSize.height / 2 - 15;
}
}
func showIndicator() {
if indicator.isHidden == false { return }
indicator.isHidden = false
indicator.image = loaderGif
indicator.startAnimating()
UIView.animate(withDuration: 0.2) {
self.contentInset.right = self.offset + 40
}
}
func hideIndicator() {
if indicator.isHidden { return }
self.indicator.isHidden = true
}
func resetContentInset(animate:Bool = true) {
UIView.setAnimationsEnabled(animate)
UIView.animate(withDuration: 0.1, animations: {
self.contentInset.right = 0
}) { (success) in
if !animate {
UIView.setAnimationsEnabled(true)
}
}
}
}
In ViewController
//Table View Delegate
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let homeTableViewCell = cell as? HorizontalScrollHomeTableViewCell else {
return
}
homeTableViewCell.collectionView.loadGifAnimatedIndicator()
homeTableViewCell.collectionView.reloadData()
}
//Collection View Delegate
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let horizontalCollView = collectionView as! HorizontalScrollDealCollectionView
let dataIndex = horizontalCollView.tableViewCellIndex!
let data = items[dataIndex.row]
if (indexPath.item == data.products.count - 1) && data.moreAvailable {
horizontalCollView.showIndicator()
data.loadMore(completion: {
(success) in
horizontalCollView.hideIndicator()
if success {horizontalCollView.reloadData()}
})
}
}
Screenshot 1
****Screenshot 2**
Changing
private let loaderGif = UIImage.gif(name: "831")
to
private var loaderGif: UIImage {
return UIImage.gif(name: "831")
}
solves the issue but tableview scrolling becomes laggy/jittering.
Also did this asynchronously using DispatchQueue but didn't helped. Still scrolling hangs.
func loadGifAnimatedIndicator() {
indicator.isHidden = true
DispatchQueue.main.async {
self.indicator.image = self.loaderGif
}
}
I have a TableView with cells, and one cell is holding a CollectionView.
Inside the CollectionView, I have cells with UIImageViews.
If I add new elements to the datasource while the CollectionView is visible then it works fine.
But if I scroll down in the TableView, add the new elements then scroll up, then even though it adds the new cells, they are displaying the wrong image.
Video: https://youtu.be/QwvMv2xaaAI
Code:
MainViewController(Not the whole)
func addNewPhotos(newPhotosArray: [Photo]){
var collectionViewInserts : [IndexPath] = []
for (i in 0...newPhotosArray.count) {
// I add the new photos to the datasource
PhotosStore.shared.photos.insert(newPhotosArray[i], at: 0)
// Then save the indexPath what needs to be inserted
collectionViewInserts.insert(IndexPath(row: i, section: 0), at: 0)
}
if let cell = self.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? PhotosCell {
cell.photosCollectionView.performBatchUpdates({
cell.photosCollectionView.insertItems(at: collectionViewInserts)
}, completion: nil)
}
}
extension MainViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return PhotosStore.shared.photos.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as! PhotoCell
cell.photoImageView.downloadedFrom(link: (appSettings.url + "/resources/img/wp/prev/" + PhotosStore.shared.photos[indexPath.item].fileName))
return cell
}
}
PhotosCell:
import UIKit
class PhotosCell : UITableViewCell{
#IBOutlet weak var photosCollectionView : UICollectionView!
}
extension PhotosCell {
func setCollectionViewDataSourceDelegate<D: UICollectionViewDataSource & UICollectionViewDelegate>(_ dataSourceDelegate: D, forRow row: Int) {
// IF I PLACE A .reloadData() HERE, THEN IT WORKS BUT THEN THE CELL FLICKERS/JUMPS WHEN APPEARING ON SCREEN
let itemSize = 70
photosCollectionView.delegate = dataSourceDelegate
photosCollectionView.dataSource = dataSourceDelegate
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = UICollectionViewScrollDirection.horizontal
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width: itemSize, height: itemSize)
photosCollectionView.setCollectionViewLayout(layout, animated: true)
photosCollectionView.tag = row
photosCollectionView.setContentOffset(photosCollectionView.contentOffset, animated:false) // Stops collection view if it was scrolling.
photosCollectionView.reloadData()
}
var collectionViewOffset: CGFloat {
set { photosCollectionView.contentOffset.x = newValue }
get { return photosCollectionView.contentOffset.x }
}
}
What do I wrong? I do update the datasource correctly, I do perform batch updates on the collection view to insert the correct cells..
Updated details:
MainViewController:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Photos on top
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "PhotosCell", for: indexPath) as! PhotosCell
cell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
cell.collectionViewOffset = storedPhotosCollectionViewOffset[indexPath.row] ?? 0
return cell
}
... other cells ...
}
Extension to download images: (I'm sure that's not the problem but just in case)
extension UIImageView {
func downloadedFrom(url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {
image = nil
if let cachedImage = ImageCache.shared.loadCachedImage(url: url) {
image = cachedImage
return
}
contentMode = mode
URLSession.shared.dataTask(with: url) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else {
DispatchQueue.main.async {
self.image = UIImage(named: "imageMissing")
}
return
}
DispatchQueue.main.async {
self.image = image
ImageCache.shared.cacheImage(image: image, url: url)
}
}.resume()
}
func downloadedFrom(link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
return downloadedFrom(url: url, contentMode: mode)
}
}
First in cellForRowAt
cell.photosCollectionView.reloadData()
return cell
Second you have to note that the image is downloaded ( consider a dummy image for the imageView or set a background to it ) every scroll so use SDWebImage
I would like to display multiple images on scrollview when user selected images with DKImagePickerController github.
Here is my code but images don't appear. Anyone can tell what's wrong?
Thank you in advance!
var picker = DKImagePickerController()
var imagesArray = [Any]()
#IBAction func pickPhoto(_ sender: Any) {
picker.maxSelectableCount = 10
picker.showsCancelButton = true
picker.allowsLandscape = false
picker.assetType = .allPhotos
self.present(picker, animated: true)
picker.didSelectAssets = { (assets: [DKAsset]) in
self.imagesArray.append(assets)
for i in 0..<self.imagesArray.count {
let imageView = UIImageView()
imageView.image = self.imagesArray[i] as? UIImage
imageView.contentMode = .scaleAspectFit
let xposition = self.view.frame.width * CGFloat(i)
imageView.frame = CGRect(x: xposition, y: 330, width: self.scrollView.frame.width, height: 170)
self.scrollView.contentSize.width = self.scrollView.frame.width * CGFloat(i * 1)
self.scrollView.addSubview(imageView)
}
}
}
Instead of displaying images to scrollView, you can add that images in array and reload the collectionView/TableView..
as I did it here in collectionView
func showImagePicker() {
let pickerController = DKImagePickerController()
self.previewView?.isHidden = false
pickerController.assetType = .allPhotos
pickerController.defaultAssetGroup = .smartAlbumUserLibrary
pickerController.didSelectAssets = { (assets: [DKAsset]) in
if assets.count>0
{
for var i in 0 ... assets.count-1 {
assets[i].fetchOriginalImage(true, completeBlock: { (image, info) in
self.abc.append(image)
})
}
}
self.previewView?.reloadData()
}
self.present(pickerController, animated: true) {}
}
Here abc :[UIImage] and previewView? : UICollectionView
and in collection delegate methods:
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return self.abc.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell: UICollectionViewCell?
var imageView: UIImageView?
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CellImage", for: indexPath)
imageView = cell?.contentView.viewWithTag(1) as? UIImageView
if let cell = cell, let imageView = imageView
{
let tag = indexPath.row + 1
cell.tag = tag
imageView.image = self.abc[indexPath.row]
}
return cell!
}
I did a grid (collectionView) inside a tableViewCell, the problem is loading different images per cell. Make a Json like this:
{
{
"name": "Vegetales"
"images": { imagesURLStrings }
},
{
"name": "Frutas"
"images": { imagesURLStrings }
},
}
I use this page for custom the view and this other to make the async download.
I think the problem is because, when I try to defined the quantity of cells for the collectionView inside the tableviewCell, the assignation its wrong, its not working, and I don't know how to fixed.
The code for download the images:
func loadImages() {
var section = 0
var row = 0
while (section < searchDataresults.count) {
for i in searchDataresults[section].images {
let key = section * 10 + row
let imageUrl = i
let url:URL! = URL(string: imageUrl)
task = session.downloadTask(with: url, completionHandler: { (location, response, error) -> Void in
if let data = try? Data(contentsOf: url){
// 4
DispatchQueue.main.async(execute: { () -> Void in
// 5
// Before we assign the image, check whether the current cell is visible
let img:UIImage! = UIImage(data: data)
saveImage(image: img, name: String(key))
})
}
})
task.resume()
row += 1
}
section += 1
row = 0
}
}
}
And the code were I put the images on the collectionView, remembering that it is inside a tableViewCell, so the quantity of cells have to change depending of the images.count of the json.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return searchDataresults[cellLoad].images.count
}
internal func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellImage", for: indexPath as IndexPath)
let key = cellLoad * 10 + indexPath.row
if let img = loadImage(name: String(key)) {
let imageView = UIImageView(image: img)
imageView.frame = cell.frame
imageView.bounds = cell.bounds
imageView.center = cell.center
cell.contentView.addSubview(imageView)
print(key)
} else {
let imageView = UIImageView(image: UIImage(named: "emptyImg"))
imageView.frame = cell.frame
imageView.bounds = cell.bounds
imageView.center = cell.center
cell.contentView.addSubview(imageView)
}
return cell
}
I really appreciate your help!
subclass UICollectionviewCell and reset the content of your collection view cell
override func prepareForReuse() {
super.prepareForReuse()
self.customImageview.image = nil
}
I am learning how to create a UICollectionView programmatically. I want to create a grid of pictures collected from the user in another part of the app.
Will this sample code help me accomplish this? Also, how do I configure the data to emit the image I want? My source code is below.
UICollectionView:
class PhotosViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
let imageStore = ImageStore()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)
layout.itemSize = CGSize(width: 100, height: 100)
let myCollectionView:UICollectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
myCollectionView.dataSource = self
myCollectionView.delegate = self
myCollectionView.registerClass(RDCellCollectionViewCell.self, forCellWithReuseIdentifier: "MyCell")
myCollectionView.backgroundColor = UIColor.whiteColor()
self.view.addSubview(myCollectionView)
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
var images: [UIImage] = [
]
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let myCell = collectionView.dequeueReusableCellWithReuseIdentifier("MyCell", forIndexPath: indexPath) as! RDCellCollectionViewCell
myCell.imageView.image = images[indexPath.item]
myCell.backgroundColor = UIColor.grayColor()
return myCell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
print("User tapped on item \(indexPath.row)")
}
}
ImageStore.swift:
class ImageStore: NSObject {
let cache = NSCache()
func setImage(image: UIImage, forKey key: String) {
cache.setObject(image, forKey: key)
let imageURL = imageURLForKey(key)
if let data = UIImageJPEGRepresentation(image, 0.5) {
data.writeToURL(imageURL, atomically: true)
}
}
func imageForKey(key: String) -> UIImage? {
if let existingImage = cache.objectForKey(key) as? UIImage {
return existingImage
}
let imageURL = imageURLForKey(key)
guard let imageFromDisk = UIImage(contentsOfFile: imageURL.path!) else {
return nil
}
cache.setObject(imageFromDisk, forKey: key)
return imageFromDisk
}
func deleteImageForKey(key: String) {
cache.removeObjectForKey(key)
let imageURL = imageURLForKey(key)
do {
try NSFileManager.defaultManager().removeItemAtURL(imageURL)
}
catch let deleteError {
print("Error removing the image from disk: \(deleteError)")
}
}
func imageURLForKey(key: String) -> NSURL {
let documentsDirectories =
NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
let documentDirectory = documentsDirectories.first!
return documentDirectory.URLByAppendingPathComponent(key)
}
}
You're on the right track. You'll need to create a subclass of UICollectionViewCell that contains a UIImageView; this will let you plug the correct UIImage into it in cellForItemAtIndexPath.
This describes how to hook up your custom cell:
Create UICollectionViewCell programmatically without nib or storyboard
As for getting the correct image, you'll need to map the index path to your image store somehow, so that an item number corresponds to the correct image key.
If the task is to add an image, you should use something like this in cellForItemAtIndexPath:
let myCell = collectionView.dequeueReusableCellWithReuseIdentifier("MyCell", forIndexPath: indexPath)
myCell.backgroundColor = UIColor.blueColor()
let imageView = UIImageView(frame: cell.contentView.frame)
cell.contentView.addSubview(imageView)
imageView.image = //Here you should get right UIImage like ImageStore().imageForKey("YOUR_KEY")
return myCell
Or you can use custom UICollectionViewCell subclass as Joshua Kaden wrote.