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
Related
Im making a game that has a collectionView character select at the bottom of the GameScene. Many of the characters are locked, so i display a "Locked" image as the collection view cell image. However, when the user unlocks the character and they load the GameScene again(move to gameOver scene, restart the game back to present GameScene), i need to update the collectionView cells images and swap out the "Locked" image with the regular image. Ive tried so many different things but it isn't working. It won't swap out/reload the new images. I know its just something small I'm missing but heres all my code associated with the collection view.
class CharactersCollectionViewCell: UICollectionViewCell {
//MARK : Public API
var characters : NACharacters!{
didSet{
updateUI()
}
}
//MARK : Private
#IBOutlet weak var featuredImageView: UIImageView!
private func updateUI(){
self.layer.borderColor = UIColor.whiteColor().CGColor
self.layer.borderWidth = 2.5
self.layer.masksToBounds = true
self.layer.cornerRadius = CGFloat(roundf(Float(self.frame.size.width/2.0)))
featuredImageView.image = characters.featuredImage
}
}
class NACharacters {
var featuredImage : UIImage!
static var characterLineUp = [NACharacters]()
init(featuredImage: UIImage){
self.featuredImage = featuredImage
}
static func createCharacters() -> [NACharacters]{
print("Inside create character")
characterLineUp.insert(NACharacters(featuredImage: UIImage(named: "DiploSquadCollectionView")!), atIndex: 0)
characterLineUp.insert(NACharacters(featuredImage: UIImage(named: "LockedNBAPlayersCollectionView")!), atIndex: 1)
if(NSUserDefaults().unlockNBAPlayers == true){
characterLineUp[1].featuredImage = UIImage(named: "NBAPlayersCollectionView")!
}
characterLineUp.insert(NACharacters(featuredImage: UIImage(named: "LockedLeanSquadCollectionView")!), atIndex: 2)
if(NSUserDefaults().totalDabs >= 10000){
print("setting up lean to be unlocked")
characterLineUp[2].featuredImage = UIImage(named: "LeanSquadCollectionView")!
}
return characterLineUp
}
}
//Inside Game View Controller
#IBOutlet var collectionView: UICollectionView!
//MARK : Data Source
internal var characters = NACharacters.createCharacters()
extension GameViewController : UICollectionViewDataSource, UICollectionViewDelegate{
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return characters.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(Storyboard.CellIdentifier, forIndexPath: indexPath) as! CharactersCollectionViewCell
cell.characters = self.characters[indexPath.item]
return cell
}
//Inside Game Scene
var collectionView: UICollectionView? {
return self.view?.subviews.filter{ $0 is UICollectionView }.first as? UICollectionView
}
override func didMoveToView(view: SKView) {
//INSIDE HERE I NEED TO UPDATE THE COLLECTION VIEW CELL IMAGES
}
I know this looks like a lot of code but most of it is pretty standard. I show you all of this so that we can pin point exactly whats wrong or what needs to be done to get this to work. This is all of my code associated with the collectionView.
Have you try this in same place ::
imageView.image = nil
then set the image you want to update as ..
imageView.image = UIImage(named : "YourImage")
When you need to 'refresh' a Collection View, tell it to reload its data, then all the images should update as needed.
collectionView.reloadData()
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.
With VoiceOver switched-on, when focus comes on a UIButton/UITableViewCell/UICollectionViewCell, VoiceOver reads it's accessibility label once.
Then as soon as user double taps to select that UIButton/UITableViewCell/UICollectionViewCell, VoiceOver reads the same accessibility label again besides performing action (navigation etc) on UIButton/UITableViewCell/UICollectionViewCell selection.
I've searched a lot but not able to find a way to stop/disable VoiceOver reading accessibility label on UIButton/UITableViewCell/UICollectionViewCell selection.
Any help would be highly appreciated.
Let's see how to stop the VoiceOver accessibility reading for the UIButton and the UITableViewCell elements.
UIBUTTON : just create your own button class and override the accessibilityActivate method.
class BoutonLabelDoubleTap: UIButton {
override func accessibilityActivate() -> Bool {
accessibilityLabel = ""
return true
}
}
UITABLEVIEWCELL : two steps to be followed.
Create a custom UIAccessibilityElement overriding the accessibilityActivate method.
class TableViewCellLabelDoubleTap: UIAccessibilityElement {
override init(accessibilityContainer container: Any) {
super.init(accessibilityContainer: container)
}
override var accessibilityTraits: UIAccessibilityTraits {
get { return UIAccessibilityTraitNone }
set { }
}
override func accessibilityActivate() -> Bool {
accessibilityLabel = ""
return true
}
}
Use the previous created class to implement your table view cells in the view controller.
class TestButtonTableViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var myTableView: UITableView!
#IBOutlet weak var bottomButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
myTableView.delegate = self as UITableViewDelegate
myTableView.dataSource = self as UITableViewDataSource
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let zeCell = tableView.dequeueReusableCell(withIdentifier: "myPersoCell",
for: indexPath)
zeCell.accessibilityElements = nil
var elements = [UIAccessibilityElement]()
let a11yEltCell = TableViewCellLabelDoubleTap(accessibilityContainer: zeCell)
a11yEltCell.accessibilityLabel = "cell number " + String(indexPath.row)
a11yEltCell.accessibilityFrameInContainerSpace = CGRect(x: 0,
y: 0,
width: zeCell.contentView.frame.size.width,
height: zeCell.contentView.frame.size.height)
elements.append(a11yEltCell)
zeCell.accessibilityElements = elements
return zeCell
}
}
I haven't tried for a UICollectionViewCell but it should be the same rationale as the UITableViewCell's.
Following these code snippets, you're now able to decide if VoiceOver should stop reading out the desired elements labels when selected.
Swift 5
What worked for me was setting myElementIWantSilent.accessibilityTraits = .none
EDIT: I should note that these are also present:
viewContainingSilentElement.isAccessibilityElement = true
viewContainingSilentElement.accessibilityTraits = .image
viewContainingSilentElement.accessibilityLabel = "some text i want read aloud"
iPhone 8
iOS 14.5.1
XCode 12.5
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. :)
I want a rounded image inside a UIImageView. I subclassed UITableViewCell and added the cornerRadious in awakeFromNib like this:
class FriendsCell: UITableViewCell {
#IBOutlet weak var friendImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
self.friendImageView.layer.cornerRadius = self.friendImageView.frame.size.width / 2
self.friendImageView.clipsToBounds = true
}
And this is what i get:
And this is what i expected:
The code for filling the tableView:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as FriendsCell
let user = fetchedResultsController.objectAtIndexPath(indexPath) as Friend
cell.friendImageView.image = UIImage(data: user.photo)
return cell
}
I tried also setting the cornerRadius when filling the tableView here, but i get the same result. I also tried with all the modes of contentMode (AspecFill, ScaleToFill..)
The weird thing is that in another VC i have the same layout and this is not happening.
I have the exact problem like you and I try setting this in the function of viewWillLayoutSubviews(). And then everything is ok! below is my code
override func viewWillLayoutSubviews() {
detailPortImageView.image = detailImage
detailPortImageView.layer.cornerRadius = detailPortImageView.frame.width / 2
detailPortImageView.clipsToBounds = true
detailPortImageView.layer.borderWidth = 2
detailPortImageView.layer.borderColor = UIColor.whiteColor().CGColor
}