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
}
}
Related
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
I have a UIImageView, where the image is set with a given url. Then, I set the content mode to Scale Aspect Fit. This works fine, but there is a ton of blank space before and after the image, when the image is supposed to be directly at the top of the screen.
What I would like to do is rescale the UIImage size (maybe frame?) to match the new size created when Aspect Fit is applied (seems to be the suggestion most people received).
The problem is, whenever I test previous solutions, I'm getting a nul error. Particularly:
import UIKit
import AVFoundation
class OneItemViewController: UIViewController {
#IBOutlet weak var itemImage: UIImageView!
#IBOutlet weak var menuButton: UIBarButtonItem!
#IBOutlet weak var titleText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let imageURL:NSURL? = NSURL(string: "https://upload.wikimedia.org/wikipedia/commons/d/d5/Pic_de_neige_cordier_Face_E.jpg")
if imageURL != nil {
itemImage.sd_setImageWithURL(imageURL)
itemImage.contentMode = UIViewContentMode.ScaleAspectFit
AVMakeRectWithAspectRatioInsideRect(itemImage.image!.size, itemImage.bounds)
/**
let imageSize:CGSize = onScreenPointSizeOfImageInImageView(itemImage)
var imageViewRect:CGRect = itemImage.frame
imageViewRect.size = imageSize
itemImage.frame = imageViewRect
**/
}
if self.revealViewController() != nil {
menuButton.target = self.revealViewController()
menuButton.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
self.titleText.text = "Title: " + "Earl and Countess of Derby with Edward, their Infant Son, and Chaplain"
// Do any additional setup after loading the view.
}
/**
func onScreenPointSizeOfImageInImageView(imageV: UIImageView) -> CGSize {
var scale: CGFloat
if (imageV.frame.size.width > imageV.frame.size.height) {
if (imageV.image!.size.width > imageV.image!.size.height) {
scale = imageV.image!.size.height / imageV.frame.size.height
} else {
scale = imageV.image!.size.width / imageV.frame.size.width
}
} else {
if (imageV.image!.size.width > imageV.image!.size.height) {
scale = imageV.image!.size.width / imageV.frame.size.width
} else {
scale = imageV.image!.size.height / imageV.frame.size.height
}
}
return CGSizeMake(imageV.image!.size.width / scale, imageV.image!.size.height / scale)
}
**/
}
Tried two things here to get rid of blank space.
First attempt is the call to AVMakeRectWithAspectRatioInsideRect.
Second attempt is the two chunks of code in the /** **/ comments. (onScreenPointSizeOfImageInImageView function and calls to it in viewDidLoad.)
But I can't tell if either work because itemImage.image!.size is causing an error.
So two questions:
1) Why is itemImage.image!.size giving me a nil while unwrapping?
2) Has anyone found a faster solution to removing blank spaces caused by AspectFit?
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: image.size.width / image.size.height).isActive = true
This answer is answered programmatically with UIKit with Swift 5
As mentioned by #Ignelio, using NSLayoutConstraint would do the work for UIImageView.
The reasoning is that you want to keep maintain the aspect ratio - by using
// let UIImage be whatever you decide to name it
UIImage.contentMode = .scaleAspectFit
would make the UIImage inside the UIImageView fit back to its ratio size given the width. However, as mentioned in Apple's documentation, that will leave remaining area with transparent spacing. Hence, what you want to tackle is UIImageView's size/frame.
--
With this method, you're giving your UIImageView its width constraint equal to the UIImage's ratio - scaling back perfectly in regards to its parent's width constraint (whatever that may be).
// let UIImageView be whatever you name
UIImageView.widthAnchor.constraint(equalTo: UIImageView.heightAnchor, multiplier: UIImage.size.width / UIImage.size.height).isActive = true
I recently switched from Objective C to Swift, so I am not familiar with finding mistakes in my own algorithm. I would like to understand what am I missing here.
I've got ViewController with ScrollView added. Everything compiles and runs, no SIGARBT's or so, but It won't display the desired images. Every IBOutlet is connected propely in its place.
Below is the function for all of the UI Elements:
func UIUpdate() {
guard let imageContent = content as? ImageContent,
pages = imageContent.pages?.array as? [Image] else {
return
}
self.pages = pages
self.mainTitleLabel.text = imageContent.title
self.pageControl.numberOfPages = pages.count
let imageWidth = UIScreen.mainScreen().bounds.size.width// - 20
self.scrollView.frame.size.width = imageWidth
self.scrollView.frame.size.height = imageWidth / 1.883
self.scrollView.contentSize = CGSizeMake(imageWidth *
CGFloat(pages.count), self.scrollView.frame.size.height)
for (index, page) in pages.enumerate() {
autoreleasepool {
guard let imageData = page.image, let image = UIImage(data: imageData) else { return }
let imageView = UIImageView(image: image)
imageView.contentMode = .ScaleAspectFit
imageView.frame.size = CGSizeMake(self.scrollView.frame.size.width, self.scrollView.frame.size.height)
self.scrollView.addSubview(imageView)
self.addConstraints(toImageView: imageView, atIndex: index, lastIndex: pages.count - 1)
}
}
}
Image class is where images and titles are parsed. So this class is perfectly fine, because I have been using it in another project. This method is the only one called for getting those images. There are methods though, but I think they've got nothing to do with my problem.
Could You guys show me the mistake! Any help would be appreciated!
I am trying to specify a word wrap around an image within a UITableViewCell. I have a UITextView and a UIImageView (on top). When there is an image present for a given cell, the image will be unhidden and the exclusion paths will be set on the textContainer within the UITextView. When there is no image, the textContainer.exclusionPaths is set to nil. The problem is that when there is no image, the exclusion path remains set and sometimes vice versa (the exclusion paths are set, but the text doesn't wrap). I am using dequeueCellWithIdentifier within the UITableView and calling my function setFields (within cellForRowAtIndexPath). When the cell gets reinitialized, the exclusion path works most of the time. I'm thinking it has to do with timing. Any help is appreciated.
func setFields(avatar:UIImage?, author:String?, text:String?, images:[AnyObject], timestamp:String?){
....
self.thumbImageView.image = nil
self.thumbImageView.imageURL = nil
self.thumbImageView.hidden = true
self.titleText.textContainer.exclusionPaths = nil
if images.count > 0 {
self.images = images
if images is [UIImage] {
self.thumbImageView.image = images[0] as? UIImage
} else {
self.thumbImageView.imageURL = NSURL(string: images[0] as String)
}
self.cycleToImage(0)
self.thumbImageView.hidden = false
let convertedFrame:CGRect = self.titleText.convertRect(self.thumbImageView.frame, fromView: self)
let exclusionPath:UIBezierPath = UIBezierPath(roundedRect: convertedFrame, cornerRadius: 0.0);
self.titleText.textContainer.exclusionPaths = [exclusionPath]
}
}
I am trying to make a table wherein first cell has a differernt layout than the rest. I want to put the image as background for first cell i.e it shd look something like this:
and here is the code for my implementation
func imageCellAtIndexPath(indexPath:NSIndexPath) -> MainTableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier(imageCellIdentifier) as MainTableViewCell
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as NSManagedObject
let eTitle:NSString = object.valueForKey("title")!.description
let deTitle = eTitle.stringByDecodingHTMLEntities()
cell.artTitle.text = deTitle
var full_url = object.valueForKey("thumbnailURL")!.description
var url = NSURL(string: full_url)
var image: UIImage?
var request: NSURLRequest = NSURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
image = UIImage(data: data)
if((indexPath.row)==0) {
var imageView = UIImageView(frame: CGRectMake(10, 10, cell.frame.width - 10, cell.frame.height - 10))
imageView.image = image
cell.backgroundView = UIView()
cell.backgroundView?.addSubview(imageView)
}
else{
cell.thumb.image = image
}
})
return cell
}
bt the problem is.. when i scroll down and scroll up back again, the background image starts repeating and the thumbnails also get overlapped as shown:
If i scroll up and down again.. this is wht happens:
i might have done some silly mistake bt m not able to figure out what it is. pls help
In table views cells are reused and you should reset parts of the cell that are not relevant the specific version of the cell style you are after. Something like:
if((indexPath.row)==0) {
let frame = CGRectMake(10, 10,
cell.frame.width - 10, cell.frame.height - 10)
var imageView = UIImageView(frame: frame)
imageView.image = image
cell.backgroundView = UIView()
cell.backgroundView?.addSubview(imageView)
// Reset
cell.thumb.image = nil
} else{
cell.thumb.image = image
// Reset
cell.backgroundView = nil
}
Even better and more idiomatic idea is to use separate UITableViewCell designs for these two cell types, each with different reuse identifiers. This way you don't need to care about resetting.
P.S. You should use dequeueReusableCellWithIdentifier:forIndexPath: instead of older dequeueReusableCellWithIdentifier: as it guarantees that a cell is returned.
The problem is that the cell is being reused by the tableView for efficiency purposes but it is never being reset.
You need to clear / remove the imageView from the background view if the cell is not at indexPath 0.