I'm going through Stanford's cs193p. Assignment 4 has us create a custom UITableVIewCell and load a picture from the web into a UIImageView inside the cell.
My UIImageView and my Cell have their content mode set to Aspect Fit on the story board.And the ImageView is set on autolayout to be hugging the cell.
And yet when the picture first loads, it will bleed out of the UIImageView. When I click on it, it will correctly aspect fit.
I tried setting the content mode in code just before assigning the image, but that also didn't work. I also tried calling layoutSubviews() and setNeedsLayout right after assigning the image, and while that helps by actually showing the image (as opposed to showing nothing until the user clicks the cell), it still shows in the wrong size until the user clicks it.
This is the code for the cell:
import UIKit
class ImageTableViewCell: UITableViewCell {
#IBOutlet weak var pictureView: UIImageView!
var pictureURL: URL? {
didSet {
fetchImage()
}
}
fileprivate func fetchImage() {
if let url = pictureURL {
pictureView.image = nil
let queue = DispatchQueue(label: "image fetcher", qos: .userInitiated)
queue.async { [weak weakSelf = self] in
do {
let contentsOfURL = try Data(contentsOf: url)
DispatchQueue.main.async {
if url == self.pictureURL {
weakSelf?.pictureView?.image = UIImage(data: contentsOfURL)
weakSelf?.layoutSubviews()
print("loaded")
}
}
} catch let exception {
print(exception.localizedDescription)
}
}
}
}
}
This is the code that loads the cell on its TableViewController:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = UITableViewCell()
switch indexPath.section {
case 0:
cell = tableView.dequeueReusableCell(withIdentifier: "imageCell", for: indexPath)
if let imageCell = cell as? ImageTableViewCell {
imageCell.pictureURL = tweet?.media[indexPath.row].url
// other stuff not programmed yet
}
return cell
The code that gives me the cell's height:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0 && tweet != nil {
let media = tweet?.media[indexPath.row]
return tableView.frame.width / CGFloat(media!.aspectRatio)
}
return UITableViewAutomaticDimension
}
I'm sorry for pasting all this code, but I have no idea where the problem is so I'm putting everything I can this might be related.
You should set content mode first and then you should set the frame of your imageview, so once you should try to set content mode in awakeFromNib of tableview subclass or from cellforrowatindexpath before setting image to it!
Or you can set your content mode from interface builder (from storyboard!) - > select your imageview - > fro attribute inspector - > select mode(under view) to Aspect fit
Well, following an answer on reddit, I deleted the table view controller and remade it, setting all the outlets again. It worked, I guess it was a problem in Xcode?
So if you're having a problem like this, try remaking your storyboard.
Related
in my View:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TransactionTableCell", for: indexPath) as! TransactionTableCell
let newItem = getTransactionsInSection(section: sectionHeader[indexPath.section])[indexPath.row]
cell.configure(item: newItem)
}
in my TransactionTableCell
func configure(item: TransactionModel) {
guard let withdrawalBonuses = item.withdrawalBonuses,
withdrawalBonuses < 0,
let accruedBonuses = item.accruedBonuses,
accruedBonuses > 0 else {
configureWithOneOperation(item)//shows one line of operation
return
}
//show 2 lines of operations
firstOperationAmountLabel.text = "+\(Int(accruedBonuses))"
secondOperationAmountLabel.text = "\(Int(withdrawalBonuses))"
}
When I scroll the cell , second operation line is appears in wrong cells where its shouldn't be, even If I reload my table , that also has this problem.
You should use prepareForReuse() method
Simply just clear data of your labels:
override func prepareForReuse() {
super.prepareForReuse()
firstOperationAmountLabel.text = nil
secondOperationAmountLabel.text = nil
}
There are few things to check here.
Make sure you reset all fields before configure a new cell.
If you have created a cell using xib or storyboard, make sure you haven't filled labels with static text.
Is your guard statements passing for every item?
Else block for guard configures cell with a single operation, Is it handling all ui elements in cell?
I've a vertical listing with 7 types of UITableViewCells. One of them consist a UITableView inside the cell.
My requirement is the main tableview should autoresize the cell according to the cell's inner tableview contentSize. That os the inner tableview will show its full length and the scrolling will be off.
This approach working fine, but for cell with tableview only. When I introduce a UIImageView (with async loading image) above inner tableview, the total height of cell is somewhat smaller than the actual height of its contents. And so the inner tableview is getting cut from bottom.
Here is a representation of the bug.
I'm setting the height of UImageView according to the width to scale properly:
if let media = communityPost.media, media != "" {
postImageView.sd_setImage(with: URL(string: media), placeholderImage: UIImage(named: "placeholder"), options: .highPriority) { (image, error, cache, url) in
if let image = image {
let newWidth = self.postImageView.frame.width
let scale = newWidth/image.size.width
let newHeight = image.size.height * scale
if newHeight.isFinite && !newHeight.isNaN && newHeight != 0 {
self.postViewHeightConstraint.constant = newHeight
} else {
self.postViewHeightConstraint.constant = 0
}
if let choices = communityPost.choices {
self.datasource = choices
}
self.tableView.reloadData()
}
}
} else {
self.postViewHeightConstraint.constant = 0
if let choices = communityPost.choices {
datasource = choices
}
tableView.reloadData()
}
And the inner table view is a subclass of UITableView :
class PollTableView: UITableView {
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return self.contentSize
}
override var contentSize: CGSize {
didSet{
self.invalidateIntrinsicContentSize()
}
}
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
}
The main table view is set to resize with automaticDimension :
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cellHeights[indexPath] = cell.frame.size.height
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return cellHeights[indexPath] ?? UITableView.automaticDimension
}
Can't seem to understand what is going wrong. Any help is appreciated.
Thanks.
When the table view is asking you to estimate the row height, you are calling back the table view. Thus you are not providing it with any information it doesn't already have.
The problem is probably with your async loading image, so you should predict the image size and provide the table view with properly estimated row height when the image hasn't loaded yet.
When I run my app on the simulator the images that I load via a http request don't have proper sizing and bounds clip. I want them to a size 60x60 with a round shape, but instead they scale to fit the UITableViewCell kinda randomly but after I scroll up and down they remain fixed but still to big, I don't know what causes this neither do I know how to fix it, I'm new to iOS.I will post a screenshot with my UIImageView in Table Cell and with the effect that it has when I first run the app and my View Controller class.
I have tried to mess with the constraints, set fixed width and height constraint on the UIImageView but with no result.
I also tried to disable subview auto resize from the cell view but with no result.
This is the effect,this happens before I start scrolling:
This happens after I scroll up and down,the clipping on bounds returns to normal but the size is still to big:
This is my storyboard with the cell image view:
And this is my ViewController.swift class :
//
// ViewController.swift
// TopDevelopers
//
// Created by Eduard Valentin on 12/04/2018.
// Copyright © 2018 Eduard Valentin. All rights reserved.
//
import UIKit
import Alamofire
import Foundation
struct UserInfo {
var name:String
var imageURL:String
var imageView:UIImageView
init(newName: String, newImageURL:String, newImageView: UIImageView) {
self.name = newName
self.imageURL = newImageURL
self.imageView = newImageView
}
}
class ViewController: UIViewController,UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var tableView: UITableView!
var users:[UserInfo] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// GET the data from the stackexchange api
let param: Parameters = [
"order": "desc",
"max" : 10,
"sort" : "reputation",
"site" : "stackoverflow"
]
Alamofire.request("https://api.stackexchange.com/2.2/users", method: .get, parameters: param).responseJSON { (response) -> (Void) in
if let json = response.result.value {
// we got a result
/* I know this is a bit ugly */
let json1 = json as! [String:AnyObject]
let usersInfoFromJSON = json1["items"] as! NSArray // remember to cast it as NSDictionary
for userInfo in usersInfoFromJSON {
let userDict = userInfo as! NSDictionary
Alamofire.request(userDict["profile_image"] as! String).responseData { (response) in
if response.error == nil {
print(response.result)
// Show the downloaded image:
if let data = response.data {
let imageView = UIImageView()
imageView.image = UIImage(data: data)
self.users.append(UserInfo(newName: userDict["display_name"] as! String,
newImageURL: userDict["profile_image"] as! String,newImageView: imageView))
self.tableView.reloadData()
}
}
}
}
}
}
}
#available(iOS 2.0, *)
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.users.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)
#available(iOS 2.0, *)
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell") as! CustomTableViewCell
cell.cellImageView.image = self.users[indexPath.row].imageView.image
cell.cellImageView.layer.cornerRadius = (cell.cellImageView.layer.frame.height / 2)
cell.cellLabel.text = self.users[indexPath.row].name
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
EDIT 1:
I also tried to set content mode to "scale to fit", "aspect fit" still the same results.
EDIT 2: Ok, I solved it by just deleting almost everything and doing it all over again but this time I did set the option for the "Suggested constraints", also I used xib's for the cells and everything is normal now, I still don't know what caused it.
Set clipsToBounds property to true, and set frames according to cellImageView's frame rather cellImageView.layer's frame:
cell.cellImageView.clipsToBounds = true
cell.cellImageView.layer.masksToBounds = true
cell.cellImageView.contentMode = .scaleAspectFit
cell.cellImageView.layer.cornerRadius = cell.cellImageView.frame.height / 2
And try to add UIImage in your struct rather than UIImageView. And use in cellForRowAtIndexPath as:
cell.cellImageView.image = self.users[indexPath.row].image
In your case do not use CustomCell, because basic tableviewcell provide default UIImageView and UILable
Update inside cellForRowAt function with below code
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellIndentifier")
cell.imageView?.contentMode = .scaleAspectFit
cell.imageView?.image = self.users[indexPath.row].imageView.image
cell.imageView?.layer.cornerRadius = cell.imageView?.frame.width / 2
cell.imageView?.layer.masksToBounds = true
return cell
}
This is my requirement:
I want my tableView's cell to be like the last cell, its border is margin the tableView some pix, not contradict the tableview's edge.(I want this is because when I click down the cell, there is gray effect on the cell)
How to do with that?
u can't resize the cell's, instead u can set the views's layer properties to achieve the similar effect, for example, (u are not mentioning which language u are using, i assume u are using swift).
i will assume your custom cell contains a UIView and some other view components, like below,
and also add outlet for imageHolderView in the above image,
out let name will be holderView as shown in below image,
in the custom cell class, define two methods for selection management, and your custom cell class would look like below,
class CustomCell: UITableViewCell {
#IBOutlet weak var circleNameTextField: UILabel!
#IBOutlet weak var holderView: UIView!
var cellindexPath:IndexPath?
var selectedIndexPath:IndexPath?
func selectTheCell() {
if self.selectedIndexPath?.row == self.cellindexPath?.row {
self.holderView.layer.cornerRadius = 6.0
self.holderView.layer.masksToBounds = true
self.holderView.layer.borderWidth = 4.0
self.holderView.layer.borderColor = UIColor.red.cgColor
self.backgroundColor = UIColor.gray
} else {
self.resetCellWith(animate: false)
}
}
func resetCellWith(animate:Bool) {
self.holderView.layer.cornerRadius = 0.0
self.holderView.layer.masksToBounds = false
self.holderView.layer.borderWidth = 0.0
self.holderView.layer.borderColor = UIColor.clear.cgColor
self.backgroundColor = UIColor.orange
}
}
now all u have to do is call the above methods, from controller and update the cell behaviour, for example,
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selIndexPath = indexPath
self.aTableView.reloadSections(IndexSet(integer: 0), with: .none)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : CustomCell? = tableView.dequeueReusableCell(withIdentifier: "CUSTOM_CELL", for: indexPath) as? CustomCell//tableView.dequeueReusableCell(withIdentifier: "CUSTOM_CELL") as? CustomCell
cell?.cellindexPath = indexPath
if let selectedIndexPath = self.selIndexPath {
cell?.selectedIndexPath = selectedIndexPath
cell?.selectTheCell()
} else {
cell?.resetCellWith(animate:false)
}
cell?.selectionStyle = .none
return cell!
}
with the above arrangement, u can get the table cell and selection like below,
NOTE: well, above is one way achieve this effect. and method names i simply used the sample project that i created for different purpose. :)
I have seen this issue a lot of places but have yet to come across a solution that works for me. I have a custom UITableViewCell, in which I have placed a UIImageView. The Image view is supposed to hug the right side of the cell (with constraints from an xib file). Here is the code for how the cell is created and then formatted:
class PlaylistCell: UITableViewCell {
#IBOutlet var imView: UIImageView?
#IBOutlet var label: UILabel?
var playlist:SPTPartialPlaylist? {
didSet {
self.configure()
}
}
func configure()
{
self.imView?.clipsToBounds = true
self.label?.text = self.playlist?.name
let uri = (self.playlist?.images[0] as! SPTImage).imageURL
dispatch_async(dispatch_get_main_queue(), {
let data = NSData(contentsOfURL: uri!)
if (data != nil) {
self.imView?.image = UIImage(data: data!)
self.layoutSubviews()
}
})
}
And in my ViewController that has the table view in it:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("PlaylistCell") as! PlaylistCell
cell.playlist = self.playlists[indexPath.row]
cell.imView?.image = UIImage(named: "placeholder")
return cell
}
Everything loads correctly and the cells look fine, however when one of the cells is touched, the image snaps to the left side of the cell and decreases in size. Does anyone know why this might be happening? (PS I have tried using SDWebImage and the same issue ensues)
Can you try to do add that in your PlayListCell?
override func didMoveToSuperview() {
self.layoutIfNeeded()
}