Scrolling is very choppy in UITableView when loading images - ios

I am creating an application that Holds about 26 images inside of a structure, and then I load those images into a table view, and anytime a new image comes into the View of the simulator, there is a bit of lag. It is definitely a problem dealing with loading images inside of the Table View.
I think I need to add an async, but I have no idea how to do that. If someone could guide me in the right direction on what I should do to achieve smooth scrolling, that would be awesome!
I wish I could post a photo, but I don't have a good enough reputation on StackOverFlow.com.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
var question : Question
question = Questions[indexPath.row]
var titleLabel = cell.contentView.viewWithTag(1) as UILabel
titleLabel.text = question.name
var backgroundImage = cell.contentView.viewWithTag(5) as UIImageView
backgroundImage.image = UIImage(named: question.pic)
var frame : CGRect = CGRectMake(0, backgroundImage.frame.size.height - 200, backgroundImage.frame.size.width, backgroundImage.frame.size.height - 200)
cell.layer.shouldRasterize = true
cell.layer.rasterizationScale = UIScreen.mainScreen().scale
return cell
}

It has to do with the size of the images. Use a smaller thumbnails for the tableView cells. Convert your images smaller for it.
CGSize destinationSize = CGSizeMake(160, 160);
UIGraphicsBeginImageContext(destinationSize);
[originalImage drawInRect:CGRectMake(0,0,destinationSize.width,destinationSize.height)];
UIImage *thumbnailImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Kreuzberg's answer solved my problem, as I was getting jerky performance even with GCD and main_queue implemented. Since I can't add too much code in comments, I expanded on his answer so the width/height could be a bit more dynamic.
if (image.size.width > MAX_THUMBNAIL_RESOLUTION || image.size.height > MAX_THUMBNAIL_RESOLUTION) {
int width;
int height;
CGFloat ratio = image.size.width/image.size.height;
if (ratio > 1) {
width = MAX_THUMBNAIL_RESOLUTION;
height = width / ratio;
}
else {
height = MAX_THUMBNAIL_RESOLUTION;
width = height * ratio;
}
CGSize destinationSize = CGSizeMake(width, height);
UIGraphicsBeginImageContext(destinationSize);
[image drawInRect:CGRectMake(0,0,destinationSize.width,destinationSize.height)];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
You can make the constant MAX_THUMBNAIL_RESOLUTION to be anything you think reasonable. Or, you can calculate screen density and use a multiplier.

Related

UITableViewCell Height Issue

I'm actually trying to make the image view height dynamic
I have tried UITableViewAutomaticDimension
in the cell class I have set the image's dynamic height based on the aspect constraint
Well, you can't achieve the dynamic height of cell with UITableViewAutomaticDimension on the basis of image's constraints.
BUT image's height and width can help you in this. There is a plenty of help regarding this issue is available in following answer:
Dynamic UIImageView Size Within UITableView
make sure you have set top-bottom constraints in storyboard.
save the thumbnail image as data to local storage (I'm using core data)
using the thumb image, calculate the aspect of the image
customise the row height as you want in heightForRowAt method.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let entity = self.fetchedResultController.object(at: indexPath as IndexPath) as! Message
var newh:CGFloat = 100.00
if let fileThumbnails = entity.file_thumbnails as? NSArray{
if fileThumbnails.count != 0{
fileThumbnails.map { (thumbNail) in
newh = self.getImageHeight(image:UIImage(data: thumbNail as! Data)! , h: UIImage(data: thumbNail as! Data)!.size.height , w: UIImage(data: thumbNail as! Data)!.size.width)
}
}
}
if entity.fileStatus == "DeletedMedia" {
newh = 100
}
if entity.fileStatus == nil{
newh = 0.0
}
print ("newH " , newh)
return newh
}
func getImageHeight(image : UIImage, h : CGFloat, w : CGFloat) -> CGFloat {
let aspect = image.size.width / image.size.height
var newH = (messagesTV.frame.size.width * 0.6) / aspect
// customise as you want in newH
if(newH > 500){
newH = 500
//maximum height 500
}
if(newH < 100){
newH = 100
//minimum height = 100
}
return newH
}
if the thumb image is deleted in local storage then a placeholder image will be shown.
you can customise the newHvariable to get the desired output

Image not show properly, I want to show like Facebook image post

I have to tried all type of content mode, Third party and lots of googling.
But, not success for me.
I want to show like Facebook image post.
We are post image in facebook, facebook show properly images and fill the image very properly into image view.
Follow steps
setImage to UIImageView (with UIViewContentModeScaleAspectFit)
get imageSize (CGSize imageSize = imageView.image.size)
UIImageView resize. [imageView sizeThatFits:imageSize]
move position where you want.
CGSize imageSize = imageView.image.size;
[imageView sizeThatFits:imageSize];
CGPoint imageViewCenter = imageView.center;
imageViewCenter.x = CGRectGetMidX(self.contentView.frame);
[imageView setCenter:imageViewCenter];
set UIImageView size == image (ratio):
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
#IBOutlet weak var widthConstraint: NSLayoutConstraint!
func setImage(image: UIImage) {
imageView.image = image
let screenSize = UIScreen.main.bounds.size
let imageAspectRatio = image.size.width / image.size.height
let screenAspectRatio = screenSize.width / screenSize.height
if imageAspectRatio > screenAspectRatio {
widthConstraint.constant = min(image.size.width, screenSize.width)
heightConstraint.constant = widthConstraint.constant / imageAspectRatio
}
else {
heightConstraint.constant = min(image.size.height, screenSize.height)
widthConstraint.constant = heightConstraint.constant * imageAspectRatio
}
view.layoutIfNeeded()
}
reference link AutoLayout: UIImageView and Aspect Fit content mode

How to resize an UIImageView (swift)

I'm a beginner with swift and I can't get the resize of a UIImageView working.
Here is my current layout :
What I want is to resize the images to occupy half the screen's width (2 images per row).
Here is my storyboard :
Here is the function drawing the collection view inside my controller :
func collectionView(collectionView: UICollectionView!, cellForItemAtIndexPath indexPath: NSIndexPath!) -> UICollectionViewCell! {
var cell : MenuInspirationCellView = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as MenuInspirationCellView
cell.imageView.layer.borderWidth = 2
cell.imageView.layer.borderColor = UIColor.yellowColor().CGColor
//This is my first approach trying to modify the frame :
cell.imageView.frame = CGRectMake(0,0, self.view.bounds.width / 2,v120)
var cellImage : UIImage = UIImage(data: NSData(contentsOfURL: NSURL(string: images[indexPath.row])))
cell.imageView.image = cellImage;
//This is my second approach (based on http://www.snip2code.com/Snippet/89236/Resize-Image-in-iOS-Swift) :
// to resize an image to dimension 52x52
//var newSize:CGSize = CGSize(width: 52,height: 52)
//let rect = CGRectMake(0,0, newSize.width, newSize.height)
//UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
// image is a variable of type UIImage
//cellImage.drawInRect(rect)
//let newImage = UIGraphicsGetImageFromCurrentImageContext()
//UIGraphicsEndImageContext()
// resized image is stored in constant newImage
//cell.imageView.image = newImage;
//This is my thrid approach (based on https://gist.github.com/hcatlin/180e81cd961573e3c54d, of course i added his functions but I don't show them here for the sake of readability) :
//cell.imageView.image = self.RBSquareImageTo(cellImage, size: CGSize(width: 80, height: 80))
return cell
}
Any leads to sort this out ?
SWIFT:
func imageResize(imageObj:UIImage, sizeChange:CGSize)-> UIImage {
let hasAlpha = false
let scale: CGFloat = 0.0 // Automatically use scale factor of main screen
UIGraphicsBeginImageContextWithOptions(sizeChange, !hasAlpha, scale)
imageObj.drawInRect(CGRect(origin: CGPointZero, size: sizeChange))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext() // !!!
return scaledImage
}
The sizes of your cells are decided by your UICollectionViewLayout object, which is a UICollectionViewFlowLayout in your case. If you want all of your cells to be the same size, you can configure the properties on the layout itself; itemSize and minimumInterimSpacing will do the trick. Alternatively, if you want your cells to be different sizes, say depending on the image each contains or whatever, you needs your collection view delegate to implement the UICollectionViewDelegateFlowLayout method:
optional func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize
returning the size you want for each cell.
Following this tutorials you can resize UIImage, In that tutorial described fully .. Image Resize in swift ios8
func imageResize (imageObj:UIImage, sizeChange:CGSize)-> UIImage{
let hasAlpha = false
let scale: CGFloat = 0.0 // Automatically use scale factor of main screen
UIGraphicsBeginImageContextWithOptions(sizeChange, !hasAlpha, scale)
imageObj.drawInRect(CGRect(origin: CGPointZero, size: sizeChange))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
return scaledImage
}
Here you can also see many tutorials about Swift language Visit Here iPhone & iPad Application Development Help World

How to change UITableViewCell Image to Circle in UITableView

My code works for the most part.
The issue I am having is the images start out as square, and change to circle when I scroll the table or refresh it.
What am I missing?
Here's my code:
cell.imageView.layer.cornerRadius = cell.imageView.frame.size.width / 2.0;
cell.imageView.layer.borderWidth = 3.0;
cell.imageView.layer.borderColor = [UIColor colorWithRed:157.0/255.0 green:34.0/255.0 blue:53.0/255.0 alpha:1.0]
.CGColor;
cell.imageView.clipsToBounds = YES;
NSDictionary *tweet = (self.results)[indexPath.row];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSString * imageString = [tweet valueForKeyPath:#"user.profile_image_url"];
NSData * imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString:imageString]];
if (imageData != nil)
{
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = [UIImage imageWithData: imageData];
[cell setNeedsLayout];
});
}
});
EDIT ---- The code is in cellForRowAtIndexPath
Right when I launch the app the cell images are square. If I pullToRefresh the change to round or if I start scrolling the table they change to round.
EDIT TWO
Another issue I am seeing is the images change as I scroll.
How do I prevent this?
In order if anyone having this problem, here is the solution in
Swift
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:ContactCellTableViewCell? = tableView.dequeueReusableCellWithIdentifier("contactCell", forIndexPath: indexPath) as? ContactCellTableViewCell
var contact : ContactStruct
image = searchResults[indexPath.row]
let newImage = resizeImage(image, toTheSize: CGSizeMake(70, 70))
var cellImageLayer: CALayer? = cell?.imageView.layer
cellImageLayer!.cornerRadius = 35
cellImageLayer!.masksToBounds = true
cell?.imageView.image = newImage
return cell!
}
As you might noticed the 35 hardcode is basically half of the image size which is 70 in here.
And here is the resize function:
func resizeImage(image:UIImage, toTheSize size:CGSize)->UIImage{
var scale = CGFloat(max(size.width/image.size.width,
size.height/image.size.height))
var width:CGFloat = image.size.width * scale
var height:CGFloat = image.size.height * scale;
var rr:CGRect = CGRectMake( 0, 0, width, height);
UIGraphicsBeginImageContextWithOptions(size, false, 0);
image.drawInRect(rr)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
return newImage
}
Note: I am using a custom cell class as below:
import UIKit
class ContactCellTableViewCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var contactImage: UIImageView!
#IBOutlet weak var phoneLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
It really took me a while to figure this one out. The images become rounded after scrolling at first, but now using this code you are going to have the rounded images inside the cell from beginning.
Hope it helped anyone.
Try this in override-
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// image Width(84) / 2 = 42
cell.CellImage.layer.cornerRadius = 42.0
cell.CellImage.layer.masksToBounds = true
//or
cell.CellImage.layer.cornerRadius = cell.CellImage.frame.width / 2
cell.CellImage.clipsToBounds = true
}
Most likely the cell's frame (and hence the imageView's frame) isn't set the very first time the cell is created. So setting the imageView's layer's cornerRadius based on that initial frame doesn't work. The best solution is to implement the tableView:willDisplayCell:forRowAtIndexPath: delegate method and set the layer there:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.imageView.layer.cornerRadius = cell.imageView.frame.size.width / 2.0;
}
Its simple..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.ImageView.layer.cornerRadius = 150.0f;
cell.ImageView.layer.borderWidth = 2.0f;
cell.ImageView.layer.borderColor = [UIColor blackColor].CGColor;
cell.ImageView.clipsToBounds = YES;
}
Refer this link for more information: http://ios-blog.co.uk/tutorials/quick-tips/how-to-create-rounded-avatars-in-your-ios-application/
Create a custom cell, and add an image view (with an IBOutlet), sized and positioned however you want (the outlet is "iv" in my example below). Put the code to make image view round in the awakeFromNib method (assuming your cell is created in a nib or storyboard).
-(void)awakeFromNib {
self.iv.layer.cornerRadius = self.iv.frame.size.width / 2.0;
self.iv.layer.borderWidth = 3.0;
self.iv.layer.borderColor = [UIColor colorWithRed:157.0/255.0 green:34.0/255.0 blue:53.0/255.0 alpha:1.0].CGColor;
self.iv.clipsToBounds = YES;
}
I had a similar issue, my UIImageView was initially a square and would only show as circular when the UITableViewCell it was within was either highlighted or selected.
It was a simple fix:
Swift 2.0
imgView.layer.cornerRadius = imgView.frame.width / 2
imgView.clipsToBounds = true
This was placed inside the awakeFromNib() for my custom UITableViewCell.
This is assuming imgView is the name of the UIImageView hooked up via storyboard inside this custom UITableViewCell.
#interface MyCell : UITableViewCell
#end
#implementation MyCell
- (void)awakeFromNib
{
[super awakeFromNib];
self.imageView.layer.masksToBounds = YES;
self.textLabel.font = [UIFont systemFontOfSize:17];
self.textLabel.textColor = [UIColor darkTextColor];
}
// --- image view frame is empty rect inside tableView: willDisplayCell:
// +++ set cornerRadius inside layoutSubviews works.
- (void)layoutSubviews
{
[super layoutSubviews];
// layout image view
CGRect vfr = self.frame;
CGRect imgvr = self.imageView.frame;
imgvr.origin.x = 16;
imgvr.size.width = CGRectGetHeight(vfr);
self.imageView.frame = imgvr;
// update corner radius
self.imageView.layer.cornerRadius = imgvr.size.width * 0.5f;
// layout label
CGRect lblr = self.textLabel.frame;
lblr.origin.x = CGRectGetMaxX(imgvr) + 16;
lblr.size.width = CGRectGetWidth(vfr) - CGRectGetMaxX(imgvr) - 32;
self.textLabel.frame = lblr;
}
#end
In case your cell is by code:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let myImageView = (cell as! MyCustomCell).myImageView
myImageView.layoutIfNeeded()
myImageView.layer.cornerRadius = myImageView.frame.width / 2.0
}
cell.imageView?.layer.cornerRadius = 22.0
cell.imageView?.layer.masksToBounds = true
Set the image view to a circle inside the async call where you set the image.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSString * imageString = [tweet valueForKeyPath:#"user.profile_image_url"];
NSData * imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString:imageString]];
if (imageData != nil)
{
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = [UIImage imageWithData: imageData];
cell.imageView.layer.cornerRadius = cell.imageView.frame.size.width / 2.0;
cell.imageView.layer.borderWidth = 3.0;
cell.imageView.layer.borderColor = [UIColor colorWithRed:157.0/255.0 green:34.0/255.0 blue:53.0/255.0 alpha:1.0]
.CGColor;
cell.imageView.clipsToBounds = YES;
[cell setNeedsLayout];
});
}
});
Here is a perfect and state away solution for circular image in UITableview Cell.
Simply modify your UITableviewCell (custom cell) class with below code.
override func awakeFromNib() {
super.awakeFromNib()
imgEvent.layer.frame = (imgEvent.layer.frame).insetBy(dx: 0, dy: 0)
imgEvent.layer.borderColor = UIColor.gray.cgColor
imgEvent.layer.cornerRadius = (imgEvent.frame.height)/2
imgEvent.layer.masksToBounds = false
imgEvent.clipsToBounds = true
imgEvent.layer.borderWidth = 0.5
imgEvent.contentMode = UIViewContentMode.scaleAspectFill
}
It will also helps to solve the problem of image circular only after scrolling table..(if any)

How can I get the height and width of an uiimage?

From the URL Image in Mail
I'm adding image to mail view. It will show full image. But I want to calculate, proportionally change the height and width of the image.
How can I get the height and width of UIImage?
let heightInPoints = image.size.height
let heightInPixels = heightInPoints * image.scale
let widthInPoints = image.size.width
let widthInPixels = widthInPoints * image.scale
Use the size property on the UIImage instance. See the documentation for more details.
UIImage *img = [UIImage imageNamed:#"logo.png"];
CGFloat width = img.size.width;
CGFloat height = img.size.height;
Some of the anwsers above, don't work well when rotating device.
Try:
CGRect imageContent = self.myUIImage.bounds;
CGFloat imageWidth = imageContent.size.width;
CGFloat imageHeight = imageContent.size.height;
There are a lot of helpful solutions out there, but there is no simplified way with extension. Here is the code to solve the issue with an extension:
extension UIImage {
var getWidth: CGFloat {
get {
let width = self.size.width
return width
}
}
var getHeight: CGFloat {
get {
let height = self.size.height
return height
}
}
}
UIImageView *imageView = [[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"MyImage.png"]]autorelease];
NSLog(#"Size of my Image => %f, %f ", [[imageView image] size].width, [[imageView image] size].height) ;
import func AVFoundation.AVMakeRect
let imageRect = AVMakeRect(aspectRatio: self.image!.size, insideRect: self.bounds)
x = imageRect.minX
y = imageRect.minY
let imageView: UIImageView = //this is your existing imageView
let imageViewHeight: CGFloat = imageView.frame.height
let imageViewWidth: CGFloat = imageView.frame.width

Resources