I have a UITableView in a UIViewController, and 1 prototype cell which is defined in my own custom cell class. I have a UIImageView in the top right corner, with top and trailing margin constraints, as well as a fixed width and height:
In Interface Builder
The problem is, when I run the program, the TableView is created but the imageView is oversized:
At runtime
After researching some similar questions on this site, I've come to the conclusion that:
The cell's imageView only appears and is sized when you assign an
image to it.
I've already tried the following code in the custom cell class, and the cellForRowAtIndexPath function:
cell.imageView?.frame = CGRectMake(300, 52, 292, 136)
cell.imageView?.clipsToBounds = true
cell.contentView.addSubview(cell.imageView!)
Am I missing something or what do I do to get the imageView to the size I've made its constraints for?
Thank you in advance!
UIView has the attribute contentMode - try to define it as ScaleToFill:
self.someImage.contentMode = .ScaleToFill
Thanks for the help everyone!
I made a stupid mistake by referencing the custom cell's generic imageView?, rather than the IB Outlet I made under a different name.
Wrong:
cell.imageView?.image = UIImage(named: "whatever name")
Right:
cell.dogImageView.image = UIImage(named: "labrador")
Out of interest, what values are the fixed width and height constraints? It's hard to tell the difference between the interface builder and the device screenshots as the size of the view controller is different.
My suggestion would be doing away with the fixed width and height constraints, and instead:
• Pin the bottom of the image view to the bottom of the cell - this gives the image view it's height.
• Add an aspect ratio constraint to the image view - this gives the image view it's width, based on it's height.
This means that even if you end up changing the height of your cells, your image view will always fit inside of the cell.
Kudos for using prototype cells and a UITableViewCell subclass though - I agree with this method completely!
Your code seems vague although I agree with you using the custom cell class. What this does is to attempt to download all images set and if unable to download, it will replace the image with an image placeholder saved on the assets. All images have the same sizes when loaded.
Here is what you can do to make it work. Here is your custom class.
class SomeCell: UITableViewCell {
#IBOutlet weak var imgMain: UIImageView!
#IBOutlet weak var lblMain: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
imgMain.layer.cornerRadius = 5.0
imgMain.clipsToBounds = true
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func configureCell(image: UIImage, text: String){
imgMain.image = image
lblMain.text = text
}
}
Then on your ViewController
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var somePics = ["http://www.something.com/pic1.jpg","http://www.something.net/pic2.jpg","http://www.something.com/pic3.jpg","http://www.something.com/pic4.jpg","http://www.something.net/pic5.jpg"]
var someTitles = ["Picture 1", "Picture 2", "Picture 3", "Picture 4", "Picture 5"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// Do any additional setup after loading the view, typically from a nib.
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCellWithIdentifier("SomeCell") as? SomeCell {
var img: UIImage!
let url = NSURL(string: somePics[indexPath.row])!
if let data = NSData(contentsOfURL: url) {
img = UIImage(data: data)
}else {
img = UIImage(named: "somePlaceholderpic")
}
cell.configureCell(img, text: someTitles[indexPath.row])
return cell
} else {
return SomeCell()
}
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return somePics.count
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Hope this helps. :)
Related
I'm trying to apply a radius to the corner of the images showed in a table to make them look like a circle. The problem is that the images are located via autolayout, so the final size is not calculated till the view has layout all its subviews, and I need to code the circle mask after that.
The solution that came to me was to write the code inside the method tableView(... willDisplayCell ...), or inside the methods didMoveToSuperview() and layoutSubviews() on the custom UITableViewCell subclass. I have seen this solutions on some questions of this forum, but no one of them are working.
Here is the code with one of those methods commented:
import UIKit
struct Contact {
var name: String
var image: UIImage
}
class ActiveChatsController: UITableViewController {
let contacts = [
Contact(name: "Alex", image: UIImage(named: "Alex")!),
Contact(name: "Puto Albert", image: UIImage(named: "Albert")!),
Contact(name: "Golfo-man", image: UIImage(named: "Pablo")!)
]
override func viewDidLoad() {
self.navigationController!.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: AppDesign.color(withIntensity: 6)]
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contacts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Bundle.main.loadNibNamed("OpenedChatCell", owner: self, options: nil)?.first as! OpenedChatCell
cell.contactImage.image = contacts[indexPath.row].image
cell.contactName.text = contacts[indexPath.row].name
return cell
}
}
And the custom cell subclass is got from a XIB file:
import UIKit
class OpenedChatCell: UITableViewCell {
#IBOutlet weak var contactImage: UIImageView!
#IBOutlet weak var contactName: UILabel!
/*
override func didMoveToSuperview() {
self.contactImage.layer.cornerRadius = self.contactImage.frame.width / 2
self.contactImage.clipsToBounds = true
}
*/
}
If I run these codes on the simulator, I get this:
But if I delete the comments on the didMoveToSuperview() method and let it change the radius I get this:
After this, I wrote inside didMoveToSuperview():
print(self.contactImage.frame.height)
And it shows a height of 177.0, so I don't know where the error may be.
The problem might be that the table view itself is being created early, before the final setting of the frame of the image view. Thus, all your attempts to set the rounded corners are also happening too early, because they all depend on the initial formation of the table view.
The solution, in that case, would be as follows:
var didLayout = false
override func viewDidLayoutSubviews() {
if !self.didLayout {
self.didLayout = true // only need to do this once
self.tableView.reloadData()
}
}
If you have moved your code into this delegate method:
func tableView(UITableView, willDisplay cell: UITableViewCell,
forRowAt indexPath: IndexPath)
...that method will now run again for all the cells of the table, and the image view frame will be correct and the rounded corners will come out correctly.
Try to ovveride layoutSubviews instead of didMoveToSuperview:
override func layoutSubviews() {
super.layoutSubviews()
self.contactImage.layer.cornerRadius = self.contactImage.frame.width / 2
self.contactImage.clipsToBounds = true
}
Try this out. It may work
import UIKit
class OpenedChatCell: UITableViewCell {
#IBOutlet weak var contactImage: UIImageView!
#IBOutlet weak var contactName: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
setRoundedView(roundedView: customImageView)
}
func setRoundedView (roundedView:UIView) {
let saveCenter = roundedView.center
let newFrame:CGRect = CGRect(origin: CGPoint(x: roundedView.frame.origin.x,y :roundedView.frame.origin.y), size: CGSize(width: roundedView.frame.size.width, height: roundedView.frame.size.height))
roundedView.layer.cornerRadius = roundedView.frame.height/2
roundedView.frame = newFrame;
roundedView.center = saveCenter
roundedView.clipsToBounds = true
}
}
If you already know the size of your image and don't need to determine it on the fly (oftentimes this is the case with images in a table view), you can just try it like this:
let imageHeight = CGFloat(100)
imageView.layer.masksToBounds = true
imageView.layer.cornerRadious = imageHeight / 2
I have a custom cell with some simple labels and a UIImage. Everything appears to be set correctly, and stepping through the debugger shows that everything is getting a value and even using the print in the debugger shows that the labels have text. However my table view is still empty when executed. I have been looking at this for too long and cannot figure out the problem.
Here is the cell code
class CurrentFileCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var statusImage: UIImageView!
#IBOutlet weak var dateLabel: UILabel!
var currentContent: AircraftContent! {
didSet{
setStyles(Constants.appStyleSetting)
self.nameLabel.text = currentContent.contentName
self.dateLabel.text = currentContent.contentStatus
self.statusImage.image = UIImage(named: "color_label_circle_green")
}
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
private func setStyles(settings: StyleSettings) {
let tableFont = UIFont(name: settings.bodyFont, size: CGFloat(settings.tableFontSize))
nameLabel.font = tableFont
dateLabel.font = tableFont
// Text color
let tableFontColor = settings.tableFontColor
nameLabel.textColor = tableFontColor
dateLabel.textColor = tableFontColor
}
Here is the ViewController code with a tableview inside.
class CurrentFilesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var content: AircraftContent?
#IBOutlet weak var currentFiles: UILabel!
#IBOutlet weak var downloadingLabel: UILabel!
#IBOutlet weak var readyLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
self.content = loadContent()
setStyles(Constants.appStyleSetting)
//self.tableView.reloadData()
// Do any additional setup after loading the view.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CurrentFileCell", forIndexPath: indexPath) as? CurrentFileCell
cell?.currentContent = content
return cell!
}
func loadContent() -> AircraftContent {
return (NSKeyedUnarchiver.unarchiveObjectWithFile(AircraftContent.ArchiveURL.path!) as? AircraftContent)!
}
private func setStyles(settings: StyleSettings) {
let titleFont = UIFont(name: settings.bodyFont, size: CGFloat(settings.titleFontSize))
let key = UIFont(name: settings.bodyFont, size: CGFloat(settings.tableFontSize))
currentFiles.font = titleFont
downloadingLabel.font = key
readyLabel.font = key
// Text color
let titleFontColor = settings.titleFontColor
currentFiles.textColor = titleFontColor
downloadingLabel.textColor = titleFontColor
readyLabel.textColor = titleFontColor
}
Here are some images showing the debug location where the cell is not empty, and also printing out the label which has a value, but isn't being shown during simulation.
http://imgur.com/a/dBkpe
This is an image showing the prototype cell. The cell has the correct class set as well as the identifier.
http://imgur.com/PKtFTeQ
Lastly another image showing that the prototype cell is linked to the labels within the CurrentFileCell.
http://imgur.com/nW0QUjM
Any help at all with this would be appreciated. I have tried recreating everything but continue to be stumped as it seems like everything is how it should be.
You have to implement the 'heightForRowAtIndexPath' method for the table view
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let height:CGFloat = 75
return height
}
You may consider registering the custom class as it does not appear that you did. You can do that by using the following code in the viewDidLoad of your View Controller.
tableView.registerClass(CurrentFileCell.self, forCellReuseIdentifier: "cell")
If you are using an external nib you will want to use registerNib instead like so:
tableView.registerNib(UINib(name:"ReplaceWithYourNibName", bundle: nil), forCellReuseIdentifier: "ReuseIdentifier")
Of course, replace ReplaceWithYourNibName and ReuseIdentifier with the appropriate values. In addition, if your nib is in a different bundle specify that instead of nil (nil defaults to the main bundle).
However, do not use both registerClass and registerNib as whichever one you call last will be used and they were designed to be mutually exclusive. Whenever you make a custom UITableViewCell you must use either of the two for it to work unless you have set it explicitly in the storyboard.
Also, you could instead, use prototype cells to define your custom cell, which would, I believe, automatically register the cell. But only if you did not use prototype cells make sure to use registerClass or registerNib.
Good luck! Hope this helps!
If your cell is a static cell, then you need to comment out these methods in UITableViewDataSource:
/* override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 0
} */
I had the same issue.
Data has values and cell rows are showing empty.
I figured it by adding
contentView.addSubview(titleLabel)
where cell values are being set.
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()
}
I'm trying to follow along the following Using Auto Layout in UITableView for dynamic cell layouts & variable row heights top answer in Swift but for the IOS 7 version to mitigate some bugs in the IOS 8.
Right now, my custom cell class is:
class PostCell: UITableViewCell {
#IBOutlet var name: UILabel!
#IBOutlet var timestamp: UILabel!
#IBOutlet var postBody: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
In my Storyboard I set my custom class as the class for the cell. I don't know if I have to use my reuseIdentifier anywhere. I don't know how to get that working in the next piece of code.
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
var cell = PostCell()
var post = self.posts[indexPath.row]
cell.name.text = post.name
cell.timestamp.text = post.timestamp
cell.postBody.text = post.body
cell.setNeedsUpdateConstraints()
cell.updateConstraintsIfNeeded()
cell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(feed.bounds), CGRectGetHeight(cell.bounds))
cell.setNeedsLayout()
cell.layoutIfNeeded()
var height = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
height += 1.0
return height
}
I thought that would work, but when I call cell.name.text = post.name, I get an EXC_BAD_INSTRUCTION. I'm guessing the cell isn't properly hooked up to the storyboard somehow or something. How do I fix this?
The problem is that you don't understand how nibs work. You have set up your PostCell to be configured when it is loaded from its nib:
class PostCell: UITableViewCell {
#IBOutlet var name: UILabel!
}
So that means that name is nil - until this cell is instantiated from the nib, at which time the outlet will be hooked up. But then later you say:
var cell = PostCell()
But that is the wrong PostCell! You didn't load it from the nib; you just made one out of whole cloth. So since there was no nib-loading, the outlet (as you rightly suspect) was naturally never connected, and so cell.name is nil.
So the solution is: do not make one out of whole cloth. Load it from the nib.
I'm in trouble.I have a city list, I want to display cities in UITableView and I have disabled UITableView's scroll because i don't want to display scroll.
when I changed to UITableView's height dynamically, it doesn't changed and UITableView does not display all cities. Below the code. Please look it.
import UIKit
class FlightDetailsOneWayViewController: UIViewController {
var flightDetailArr:[FlightDetailsOneWay] = [FlightDetailsOneWay]()
#IBOutlet var tableView: UITableView!
#IBOutlet var scrollview: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
flightDetailArr.append(FlightDetailsOneWay(depCity: "London"))
flightDetailArr.append(FlightDetailsOneWay(depCity: "China"))
flightDetailArr.append(FlightDetailsOneWay(depCity: "Singapore"))
flightDetailArr.append(FlightDetailsOneWay(depCity: "Dubai"))
self.tableView.scrollEnabled = false
var cellHeight = CGFloat(self.tableView.rowHeight)
var totalCity = CGFloat(flightDetailArr.count)
var totalHeight = cellHeight * totalCity
self.tableView.frame.size.height = CGFloat(totalHeight)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return flightDetailArr.count
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
{
let cell:FlightDetailsCell = tableView.dequeueReusableCellWithIdentifier("FlightDetail") as FlightDetailsCell
let flight = flightDetailArr[indexPath.row]
cell.setCell(flight.depCity)
return cell
}
}
class FlightDetailsOneWay
{
var depCity = ""
init(depCity: String)
{
self.depCity = depCity
}
}
class FlightDetailsCell: UITableViewCell {
#IBOutlet var depCity: UILabel!
override func awakeFromNib()
{
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func setCell(depCity: String)
{
self.depCity.text = depCity
}
}
below the preview of above code.
As explanation required to this answer is very detailed.
Follow my other answer "Moving views with constraints" to get idea about how to update layout constraint.
And as per your comment about how to connect NSLayoutConstraint variable with height constraints? as below:
Go to Storyboard.
Select UIView in which you have added constraint. Click on Constraints icon to see all constraints. Right now in below image I have added only one constraint you may find more than that. Check for which constraint you have change it's properties.
Now click on view controller and Click on Show the connection inspector. here you can see all IBOutlets defined in your view controller. As in below image you can see I have created only one IBOutlet for sake of simplicity which is heightConstraint.
And this is how you can add connection to constraint variable.
You can use prototype cell if all your cell will have the same look:
a complete tutorial to make a project using prototype cell
or
a second which it more quick to read
You can can also use
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 42.0
}
This function is perfect if you resize dynamically the height cell.
You just need to use: beginUpdates() and endUpdates() for make it works.