Swift Playground UITable View Cell not resizing after I add a subview - ios

the table view loads
the sub view loads but you can see it is over other cells
sometimes the sub view loads but other cells are on top of it
I have a UITableView, and when the user touches a row a subview gets added to the screen. When they click another row, the last subview disappears (and the row should shrink back down to its normal size). The subview displays perfectly (and goes away when another row is clicked), but it hangs out of the row and covers some of the other options. Also, when the table view reloads after screen rotations, the subview ends up behind the other rows of the table.
I want the row with the subview to resize so that it matches the frame of the subview. Also, each subview is a different size depending on the text and images included in it. Because of this I have tried using auto layouts and UITableViewAutomaticDimension, but nothing seems to be working.
I am including my UITableViewController so you can see what I have and can steer me in the right direction.
class TableViewController : UITableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
tableView.backgroundColor = #colorLiteral(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
self.tableView.estimatedRowHeight = rowHeight
return filmsToDisplay.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var currentFilm = filmsToDisplay[indexPath.row]
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.selectionStyle = .none
cell.textLabel?.text = "\(currentFilm.year) - \(currentFilm.movieTitle)"
cell.textLabel?.textColor = #colorLiteral(red: 0.254901975393295, green: 0.274509817361832, blue: 0.301960796117783, alpha: 1.0)
cell.textLabel?.font = UIFont.systemFont(ofSize: fontSize)
cell.textLabel?.sizeToFit()
cell.backgroundColor = #colorLiteral(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
cell.contentView.backgroundColor = #colorLiteral(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
let accessory = makeChart(currentFilm: filmsToDisplay[indexPath.row])
cell.contentView.addSubview(accessory)
accessory.translatesAutoresizingMaskIntoConstraints = false
accessory.trailingAnchor.constraint(equalTo:cell.contentView.trailingAnchor).isActive = true
accessory.topAnchor.constraint(equalTo: cell.contentView.topAnchor).isActive = true
accessory.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor).isActive = true
accessory.widthAnchor.constraint(equalTo: cell.contentView.widthAnchor, multiplier: nomBarWidth).isActive = true
cell.contentView.translatesAutoresizingMaskIntoConstraints=false
cell.contentView.leadingAnchor.constraint(equalTo:cell.leadingAnchor).isActive = true
cell.contentView.trailingAnchor.constraint(equalTo:cell.trailingAnchor).isActive = true
cell.contentView.topAnchor.constraint(equalTo:cell.topAnchor).isActive = true
cell.contentView.bottomAnchor.constraint(equalTo: cell.bottomAnchor).isActive = true
return cell
}
var lastSelectedCell : UITableViewCell? = nil
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let currentFilm = filmsToDisplay[indexPath.row]
var detailView = createView(film: currentFilm)
detailView.tag = 555
let selectedCell:UITableViewCell = tableView.cellForRow(at: indexPath)!
//get rid of last detail view
if let lastCell = lastSelectedCell {
for i in 0..<lastCell.contentView.subviews.count {
let content = lastCell.contentView.subviews[i]
if content.tag == 555{
content.removeFromSuperview()
}
}
}
selectedCell.contentView.addSubview(detailView)
detailView.translatesAutoresizingMaskIntoConstraints=false
detailView.leadingAnchor.constraint(equalTo:selectedCell.contentView.leadingAnchor).isActive = true
detailView.trailingAnchor.constraint(equalTo:selectedCell.contentView.trailingAnchor).isActive = true
detailView.topAnchor.constraint(equalTo:selectedCell.contentView.topAnchor).isActive = true
detailView.bottomAnchor.constraint(equalTo:detailView.subviews[detailView.subviews.count - 2].bottomAnchor).isActive = true
lastSelectedCell = selectedCell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
}
The pieces of the sub view define their height as follows (there are more pieces but they all have the same kind of constraints:
//MOVIE TITLE BAR
let movieTitleBar = UIButton()
movieTitleBar.setTitleColor(#colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0), for: UIControlState.normal)
movieTitleBar.setTitle(film.movieTitle, for: UIControlState.normal)
movieTitleBar.backgroundColor = #colorLiteral(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
movieTitleBar.alpha = 0.5
movieTitleBar.layer.borderWidth = outlineWidth
movieTitleBar.layer.borderColor = #colorLiteral(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
}
filmView.addSubview(movieTitleBar)
movieTitleBar.translatesAutoresizingMaskIntoConstraints = false
movieTitleBar.topAnchor.constraint(equalTo:filmView.topAnchor).isActive = true
movieTitleBar.heightAnchor.constraint(equalToConstant: rowHeight).isActive = true
movieTitleBar.leadingAnchor.constraint(equalTo:filmView.leadingAnchor).isActive = true
movieTitleBar.trailingAnchor.constraint(equalTo:filmView.trailingAnchor).isActive = true
//POSTER IMAGE
var poster = UIButton()
isPoster = false
if let posterImg = film.image {
if let url = NSURL(string:posterImg){
if let data = NSData(contentsOf:url as URL){
film.poster = UIImage(data:data as Data)
poster.setImage(film.poster, for: UIControlState.normal)
isPoster = true
filmView.addSubview(poster)
}
}
}
//BRIEF DESCRIPTION LABEL
let briefDescriptionBar = UILabel()
briefDescriptionBar.backgroundColor = film.colorUI
briefDescriptionBar.alpha = 0.8
briefDescriptionBar.text = "\(film.year) - \(film.briefDescription)"
briefDescriptionBar.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
briefDescriptionBar.numberOfLines = 0
briefDescriptionBar.textAlignment = .center
filmView.addSubview(briefDescriptionBar)
briefDescriptionBar.translatesAutoresizingMaskIntoConstraints = false
briefDescriptionBar.leadingAnchor.constraint(equalTo:movieTitleBar.leadingAnchor).isActive = true
briefDescriptionBar.topAnchor.constraint(equalTo:movieTitleBar.bottomAnchor).isActive = true
if isPoster == true {
poster.translatesAutoresizingMaskIntoConstraints = false
poster.topAnchor.constraint(equalTo:movieTitleBar.bottomAnchor).isActive = true
poster.trailingAnchor.constraint(equalTo:movieTitleBar.trailingAnchor).isActive = true
poster.widthAnchor.constraint(equalTo:filmView.widthAnchor, multiplier:nomBarWidth).isActive = true
let aspect = (nomBarWidth * 41) / 27
poster.heightAnchor.constraint(equalTo: filmView.widthAnchor, multiplier: aspect).isActive = true
briefDescriptionBar.trailingAnchor.constraint(equalTo:poster.leadingAnchor).isActive = true
briefDescriptionBar.bottomAnchor.constraint(equalTo:poster.bottomAnchor).isActive = true
}
else {
briefDescriptionBar.widthAnchor.constraint(equalTo:filmView.widthAnchor).isActive = true
briefDescriptionBar.heightAnchor.constraint(equalTo:movieTitleBar.heightAnchor, multiplier : 2).isActive = true
}

Your problem is that you don't give a height constraint for the subviews you add , you only hook leading ,trailing , top and bottom but the contentView expects it's height from inner subviews ( if they don't have intrinsic content size like labels / buttons ) , also no need for this part of code
cell.contentView.translatesAutoresizingMaskIntoConstraints=false
cell.contentView.leadingAnchor.constraint(equalTo:cell.leadingAnchor).isActive = true
cell.contentView.trailingAnchor.constraint(equalTo:cell.trailingAnchor).isActive = true
cell.contentView.topAnchor.constraint(equalTo:cell.topAnchor).isActive = true
cell.contentView.bottomAnchor.constraint(equalTo: cell.bottomAnchor).isActive = true
Also there should be only one view inside to the contentView that is connected to the bottom , if you add more than one , then you'll get conflicts if both views are also hooked to top of it , so you have to break last view bottom constraint before hooking a new one

Related

How do I resize the header of a uitableview?

I have a uitableview
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let width = tableView.frame.width - CGFloat((start*2))
let headerView = UIView()
headerView.frame.origin.x = 0
headerView.frame.origin.y = -45
headerView.frame.size.width = tableView.frame.width
let desc = UILabel()
desc.frame.origin.x = label.frame.origin.x
desc.frame.origin.y = line.frame.maxY + 10
desc.frame.size.width = width
desc.numberOfLines = 0
desc.font = UIFont(name: "Lato-Regular", size: 17);
desc.textColor = UIColor(red: 0.53, green: 0.53, blue: 0.53, alpha: 1.00)
desc.text = self.data.desc
desc.backgroundColor = UIColor.clear
desc.textAlignment = .left
desc.adjustsFontForContentSizeCategory = true
if (loadMoreDesc) {
desc.sizeToFit()
} else {
desc.frame.size.height = 110
}
headerView.addSubview(desc)
let moreButtonDesc = UIButton(frame: CGRect(x: desc.frame.minX, y: desc.frame.maxY, width: 80, height: 15))
moreButtonDesc.contentVerticalAlignment = UIControl.ContentVerticalAlignment.center
moreButtonDesc.titleLabel?.textAlignment = .left
if (loadMoreDesc) {
moreButtonDesc.setTitle("Less", for: .normal)
} else {
moreButtonDesc.setTitle("More", for: .normal)
}
moreButtonDesc.titleLabel?.font = UIFont(name: "Lato-Regular", size: 15);
moreButtonDesc.setTitleColor(UIColor(red: 0.00, green: 0.48, blue: 1.00, alpha: 1.00), for: .normal)
moreButtonDesc.addTarget(self, action:#selector(self.expandDesc(sender:)), for: .touchUpInside)
headerView.addSubview(moreButtonDesc)
When people click on "More" button the description should expand and the height of the table view header should increase to fit the description (desc). How can I accomplish this? Here's my expandDesc function
#objc func expandDesc(sender: UIButton) {
loadMoreDesc = !loadMoreDesc
tableView.reloadData()
}
Please note that the description is of dynamic height. I won’t know the height to set for the header unless I know the height of the description.
please can you try this
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if loadMoreDesc {
return 100
} else {
return 50.0
}
}

TableView Cells not formatting correctly unless image is cached

I am having an issue with my tableview, where the cells don't orient correctly before an image is cached, and only once I return back to a page and my image is cached do they orient correctly. Here is an example of what I am talking about, the first image is when I first go to the page, and I have a function which stores images in an imagecache, and the second picture is when I return to that page after navigating somewhere else, and the images are cached.
Incorrectly Formatted:
Correctly Formatted:
I know it is a very subtle difference
with the way it looks
but I'd love to get that figured out and understand why this is happening
My cells constant height is 85, and here is the relevant code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Indexing part I left out dw about this
let cell = discountTableView.dequeueReusableCell(withIdentifier: self.discountCellId, for: indexPath) as! DiscountCell
cell.textLabel?.text = discount.retailerName
cell.detailTextLabel?.text = discount.matchedFirstName + " " + discount.matchedLastName
cell.profileImageView.image = UIImage.gif(name: "imageLoading")!
grabImageWithCache(discount.retailerImageLink) { (profilePic) in
cell.profileImageView.image = profilePic
}
//Adding second Image
cell.matchImageView.image = UIImage.gif(name: "imageLoading")!
grabImageWithCache(discount.matchedProfileImage) { (matchProfilepic) in
cell.matchImageView.image = matchProfilepic
}
//declare no selection style for cell (avoid gray highlight)
cell.selectionStyle = .none
//format the cell's curvature
cell.layer.cornerRadius = 38
return cell
}
**I am curious as to why the height of the cells seem to change when the image is cached, because the height is set to a constant number, so I have no idea why it is changing. When I print the height of the cells it says it is 85 both times as well...
UITableViewCell Class:
class DiscountCell: UITableViewCell {
override func layoutSubviews() {
super.layoutSubviews()
//vertical spacing between cells
let padding = UIEdgeInsets(top: 0, left: 0, bottom: 7, right: 0)
bounds = bounds.inset(by: padding)
textLabel?.frame = CGRect(x: 120, y: textLabel!.frame.origin.y-10, width: textLabel!.frame.width, height: textLabel!.frame.height)
detailTextLabel?.frame = CGRect(x: 120, y: detailTextLabel!.frame.origin.y, width: detailTextLabel!.frame.width, height: detailTextLabel!.frame.height)
//spacing between cells
let margins = UIEdgeInsets(top: 0, left: 0, bottom: 85, right: 0)
contentView.frame = contentView.frame.inset(by: margins)
}
//sets a placeholder imageview
let profileImageView: UIImageView = {
let imageView = UIImageView ()
imageView.image = UIImage(named: "failed")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.cornerRadius = 35
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.layer.borderWidth = 2
imageView.layer.borderColor = #colorLiteral(red: 0.9725490196, green: 0.9725490196, blue: 0.9725490196, alpha: 1)
return imageView
}()
//PlaceHolder imageview for match
let matchImageView:UIImageView = {
let imageView = UIImageView ()
imageView.image = UIImage(named: "failed")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.cornerRadius = 35
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
addSubview(matchImageView)
addSubview(profileImageView)
//Setting Profile Pic anchors
profileImageView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 5).isActive = true
profileImageView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 70).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 70).isActive = true
//Setting Match Anchors
matchImageView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
matchImageView.widthAnchor.constraint(equalToConstant: 70).isActive = true
matchImageView.heightAnchor.constraint(equalToConstant: 70).isActive = true
matchImageView.leftAnchor.constraint(equalTo: profileImageView.leftAnchor,constant: 35).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I believe I have narrowed in on the issue, I noticed that the spacing between the cells was inconsistent both times, so I think the issue is with these lines of code
//vertical spacing between cells
let padding = UIEdgeInsets(top: 0, left: 0, bottom: 7, right: 0)
bounds = bounds.inset(by: padding)
I am not sure what to do differently, as I watched tutorials saying to change the contentviews insets but when I do, it has no effect, and I see things saying iOS 11 changed the way you do this, but haven't been able to find how to actually go about fixing this.
Couple notes...
Not a good idea to modify a cell's bounds or its contentView. UIKit uses those for a lot of things (such as adjusting contents when implementing table editing).
Cell subviews should be added to the cell's contentView, not to the cell itself. Again, due to how table views rely on that hierarchy.
You can use UIView and UIImageView subclasses to handle the "rounding" for you. For example:
class RoundImageView: UIImageView {
override func layoutSubviews() {
layer.cornerRadius = bounds.size.height * 0.5
}
}
class RoundEndView: UIView {
override func layoutSubviews() {
layer.cornerRadius = bounds.size.height * 0.5
}
}
Here is a complete implementation:
class RoundImageView: UIImageView {
override func layoutSubviews() {
layer.cornerRadius = bounds.size.height * 0.5
}
}
class RoundEndView: UIView {
override func layoutSubviews() {
layer.cornerRadius = bounds.size.height * 0.5
}
}
class DiscountCell: UITableViewCell {
// "Holder" view... contains all other UI elements
// use RoundEndView so it handles the corner radius by itself
let holderView: RoundEndView = {
let v = RoundEndView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .white
return v
}()
//sets a placeholder imageview
// use RoundImageView so it handles the corner radius by itself
let profileImageView: RoundImageView = {
let imageView = RoundImageView()
imageView.image = UIImage(named: "failed")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.layer.borderWidth = 2
imageView.layer.borderColor = #colorLiteral(red: 0.9725490196, green: 0.9725490196, blue: 0.9725490196, alpha: 1)
return imageView
}()
//PlaceHolder imageview for match
// use RoundImageView so it handles the corner radius by itself
let matchImageView: RoundImageView = {
let imageView = RoundImageView()
imageView.image = UIImage(named: "failed")
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
let mainLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = UIFont.systemFont(ofSize: 17.0, weight: .regular)
return v
}()
let subLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = UIFont.systemFont(ofSize: 14.0, weight: .regular)
return v
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
backgroundColor = .clear
contentView.addSubview(holderView)
holderView.addSubview(matchImageView)
holderView.addSubview(profileImageView)
holderView.addSubview(mainLabel)
holderView.addSubview(subLabel)
NSLayoutConstraint.activate([
// holder view constraints Top 5 pts from contentView Top
holderView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5.0),
// Leading 12 pts from contentView Leading
holderView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 12.0),
// Trailing 5 pts from contentView Trailing
holderView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5.0),
// 5 pts from contentView Bottom (use lessThanOrEqualTo to avoid auto-layout warnings)
holderView.bottomAnchor.constraint(lessThanOrEqualTo: contentView.bottomAnchor, constant: -5.0),
//Setting Profile Pic anchors - Top and Leading 5 pts from Top and Leading of Holder view
profileImageView.topAnchor.constraint(equalTo: holderView.topAnchor, constant: 5),
profileImageView.leadingAnchor.constraint(equalTo: holderView.leadingAnchor, constant: 5),
// Botom 5 pts from contentView Bottom - this will determine the height of the Holder view
profileImageView.bottomAnchor.constraint(equalTo: holderView.bottomAnchor, constant: -5),
// width 70 pts, height equal to width (keeps it "round")
profileImageView.widthAnchor.constraint(equalToConstant: 70.0),
profileImageView.heightAnchor.constraint(equalTo: profileImageView.widthAnchor),
//Setting Match Pic Anchors - 35 pts from Profile Leading, centerY to Profile
matchImageView.leadingAnchor.constraint(equalTo: profileImageView.leadingAnchor, constant: 35),
matchImageView.centerYAnchor.constraint(equalTo: profileImageView.centerYAnchor, constant: 0.0),
// same width and height as Profile
matchImageView.widthAnchor.constraint(equalTo: profileImageView.widthAnchor),
matchImageView.heightAnchor.constraint(equalTo: profileImageView.heightAnchor),
//Setting Main Label Anchors - Top equal to Top of Match image
mainLabel.topAnchor.constraint(equalTo: matchImageView.topAnchor, constant: 0.0),
// Leading 12 pts from Match image Trailing
mainLabel.leadingAnchor.constraint(equalTo: matchImageView.trailingAnchor, constant: 12.0),
// prevent Trailing from going past holder view Trailing
mainLabel.trailingAnchor.constraint(equalTo: holderView.trailingAnchor, constant: -16.0),
//Setting Sub Label Anchors - Top 8 pts from Main label Bottom
subLabel.topAnchor.constraint(equalTo: mainLabel.bottomAnchor, constant: 8.0),
// Leading and Trailing equal to Main label Leading / Trailing
subLabel.leadingAnchor.constraint(equalTo: mainLabel.leadingAnchor, constant: 0.0),
subLabel.trailingAnchor.constraint(equalTo: mainLabel.trailingAnchor, constant: 0.0),
])
}
}
// example Discount object struct
struct Discount {
var retailerName: String = ""
var matchedFirstName: String = ""
var matchedLastName: String = ""
var matchedProfileImage: String = ""
var retailerImageLink: String = ""
}
class DiscountViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let discountTableView = UITableView()
let discountCellId = "dcID"
var myData: [Discount] = []
override func viewDidLoad() {
super.viewDidLoad()
// a little sample data
var d = Discount(retailerName: "Cup & Cone", matchedFirstName: "Kara", matchedLastName: "Tomlinson", matchedProfileImage: "pro1", retailerImageLink: "")
myData.append(d)
d = Discount(retailerName: "Cup & Cone", matchedFirstName: "Sophie", matchedLastName: "Turner", matchedProfileImage: "pro2", retailerImageLink: "")
myData.append(d)
d = Discount(retailerName: "Another Retailer", matchedFirstName: "WanaB3", matchedLastName: "Nerd", matchedProfileImage: "pro3", retailerImageLink: "")
myData.append(d)
discountTableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(discountTableView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
discountTableView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
discountTableView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
discountTableView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
discountTableView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
])
view.backgroundColor = UIColor(red: 1.0, green: 0.9, blue: 0.7, alpha: 1.0)
discountTableView.backgroundColor = UIColor(red: 0.66, green: 0.66, blue: 0.9, alpha: 1.0)
discountTableView.separatorStyle = .none
discountTableView.delegate = self
discountTableView.dataSource = self
discountTableView.register(DiscountCell.self, forCellReuseIdentifier: discountCellId)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Indexing part I left out dw about this
let discount = myData[indexPath.row]
let cell = discountTableView.dequeueReusableCell(withIdentifier: self.discountCellId, for: indexPath) as! DiscountCell
cell.mainLabel.text = discount.retailerName
cell.subLabel.text = discount.matchedFirstName + " " + discount.matchedLastName
// add first image
if let img = UIImage(named: "imageLoading") {
cell.profileImageView.image = img
}
// use your custom image funcs
//cell.profileImageView.image = UIImage.gif(name: "imageLoading")!
//grabImageWithCache(discount.retailerImageLink) { (profilePic) in
// cell.profileImageView.image = profilePic
//}
//Adding second Image
if let img = UIImage(named: discount.matchedProfileImage) {
cell.matchImageView.image = img
}
// use your custom image funcs
//cell.matchImageView.image = UIImage.gif(name: "imageLoading")!
//grabImageWithCache(discount.matchedProfileImage) { (matchProfilepic) in
// cell.matchImageView.image = matchProfilepic
//}
//declare no selection style for cell (avoid gray highlight)
cell.selectionStyle = .none
return cell
}
}
Results:
Your cells are a pill shape, which means you can achieve the same effect by doing cell.layer.cornerRadius = cell.frame.height / 2. Next you should use top and bottom constraints for your profileImageView so it forces the cell to have a certain padding instead of trying to create the padding yourself. The problem is that your cell height is changing a little bit when the images are cached, no idea what could be causing that.

My UITableView crashes when I add a gradient background?

I have a UITableView under a UIViewController with a custom cell. The text for the UILabel in each cell is held in an array of strings. I’m coding in Swift Playgrounds, and when I run the Playground with an empty array it works fine (there aren’t an cells, of course, but the playground does run). When I populate the array, I get the error “there as a problem running this page... check your code...”. When I step through the code, it gets stuck at the line:
view.addSubview(gradientView)
What am I doing wrong?
import UIKit
import PlaygroundSupport
class ViewController: UITableViewController {
// Array that holds menu items
var menuItems = ["option 1","option 2","option 3"]
override func viewDidLoad() {
super.viewDidLoad()
// Add a gradient background
// Set height, width to view height, width
var gradientView = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height))
let gradientLayer:CAGradientLayer = CAGradientLayer()
gradientLayer.frame.size = gradientView.frame.size
// Set colors
gradientLayer.colors = [UIColor(red: 253/255, green: 94/255, blue: 172/255, alpha: 1).cgColor, UIColor(red: 121/255, green: 73/255, blue: 242/255, alpha: 1).cgColor]
// Skew gradient (diagonally)
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 0.5, y: 1)
// Rasterize to improve performance
gradientLayer.shouldRasterize = true
//Add gradient
gradientView.layer.addSublayer(gradientLayer)
view.addSubview(gradientView)
// Reguster custom cell
tableView.register(MenuItemCell.self, forCellReuseIdentifier: "cell_1")
// Turn off seperators
tableView.separatorStyle = .none
// Set header height
tableView.sectionHeaderHeight = 75
}
// Custom header
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?{
let customView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 75))
customView.backgroundColor = .clear //UIColor(white: 0.9, alpha: 1)
let button = UIButton(type: .custom)
button.setTitle("😁", for: .normal)
button.frame = CGRect(x: 20, y: 20, width: 50, height: 50)
button.layer.cornerRadius = 25
button.layer.shadowRadius = 8.0
button.layer.shadowColor = UIColor.black.cgColor
button.layer.shadowOffset = CGSize(width: 0, height: 0)
button.layer.shadowOpacity = 0.5
let blur = UIVisualEffectView(effect: UIBlurEffect(style:
UIBlurEffect.Style.light))
blur.frame = button.bounds
blur.isUserInteractionEnabled = false //This allows touches to forward to the button.
button.insertSubview(blur, at: 0)
customView.addSubview(button)
return customView
}
#objc func updateView(){
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menuItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell_1", for: indexPath) as! MenuItemCell
cell.selectionStyle = .none
//cell.messageLabel.text = textMessages[indexPath.row]
cell.bubbleBackgroundView.backgroundColor = UIColor(white: 0.9, alpha: 1)
return cell
}
}
class MenuItemCell: UITableViewCell {
let optionLabel = UILabel()
let bubbleBackgroundView = UIView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
bubbleBackgroundView.layer.shadowOpacity = 0.35
bubbleBackgroundView.layer.shadowRadius = 6
bubbleBackgroundView.layer.shadowOffset = CGSize(width: 0, height: 0)
bubbleBackgroundView.layer.shadowColor = UIColor.black.cgColor
bubbleBackgroundView.layer.cornerRadius = 25
bubbleBackgroundView.translatesAutoresizingMaskIntoConstraints = false
addSubview(bubbleBackgroundView)
addSubview(optionLabel)
optionLabel.numberOfLines = 0
optionLabel.translatesAutoresizingMaskIntoConstraints = false
// lets set up some constraints for our label
let constraints = [optionLabel.topAnchor.constraint(equalTo: topAnchor, constant: 32),
optionLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32),
optionLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -32),
optionLabel.widthAnchor.constraint(equalToConstant: 250),
bubbleBackgroundView.topAnchor.constraint(equalTo: optionLabel.topAnchor, constant: -16),
bubbleBackgroundView.leadingAnchor.constraint(equalTo: optionLabel.leadingAnchor, constant: -16),
bubbleBackgroundView.bottomAnchor.constraint(equalTo: optionLabel.bottomAnchor, constant: 16),
bubbleBackgroundView.trailingAnchor.constraint(equalTo: optionLabel.trailingAnchor, constant: 16),
]
NSLayoutConstraint.activate(constraints)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
PlaygroundPage.current.liveView = ViewController()

Change button state if a row in tableView is selected

I have a tableView when a row is selected I would like to show a button on the bottom (outside of the tableView) to be shown (isEnabled = true) when I have no rows selected I want it to be set to disabled (isEnabled = false. How can I handle this the best?
Currently I have this logic but when I try to select multiple rows this gives me issues. Because the first select will enable it and the second will disable it.
Here is the code:
I store a true or false in a var on the top called: var someRowSelected = false
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath)
selectedCell?.contentView.backgroundColor = UIColor(red: 84.0 / 255, green: 199.0 / 255, blue: 252.0 / 255, alpha: 1)
if(!someRowSelected){
self.someRowSelected = true
nextBtn.isEnabled = true
nextBtn.backgroundColor = UIColor(red: 84.0 / 255, green: 199.0 / 255, blue: 252.0 / 255, alpha: 1)
}else{
self.someRowSelected = false
nextBtn.isEnabled = false
nextBtn.backgroundColor = UIColor.darkGray
}
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let selectedCell = tableView.cellForRow(at: indexPath)
selectedCell?.textLabel?.textColor = UIColor.darkGray
if(!someRowSelected){
self.someRowSelected = true
nextBtn.isEnabled = true
nextBtn.backgroundColor = UIColor(red: 84.0 / 255, green: 199.0 / 255, blue: 252.0 / 255, alpha: 1)
}else{
self.someRowSelected = false
nextBtn.isEnabled = false
nextBtn.backgroundColor = UIColor.darkGray
}
}
I am curious if someone can help me out.
Thank you! :)
Your code is fine however it will work if only one cell is selected, because what you are essentially doing is changing the value of the Bool to true is selected then false if deselected without checking the total number of selected rows.
The easiest way without changing a lot of your code is to add a new variable called
var rowsSelected = 0
And in your didSelectRow add one to the rowsSelected += 1
Then in your if statement check if the value of
If rowsSelected is >= 1 {
//enable and un hide your button
}
On the deselectRow do the opposite and subtract 1 from rowsSelected -= 1 then the If statement if rowsSelected == 0 { //hide the button and disable it}
I hope this helps.
Here i have made a small change in your code. See if it helps you. Create a variable on the top like someRowSelected
var indexPAthSelected : NSIndexPath?
if !someRowSelected {
self.someRowSelected = true
nextBtn.isEnabled = true
nextBtn.backgroundColor = UIColor(red: 84.0 / 255, green: 199.0 / 255, blue: 252.0 / 255, alpha: 1)
indexPAthSelected = indexPath
} else if indexPAthSelected == indexPath {
self.someRowSelected = false
nextBtn.isEnabled = false
nextBtn.backgroundColor = UIColor.darkGray
} else {
}

Views from UITableView disappearing

I have a custom cell that is showing 14 different views. Depending on what data the cell is receiving it should show the views that are equal to the .count of some data, and the color should change depending on what data it is.
For instance:
If it is receiving three types of data, it should only show 3 views. Could be ice cream, candy, marshmellows. Then it would show three views, in orange (ice cream), blue (candy), green (marshmellows).
I got that working pretty well and I am excited about it. The problem is, if I have a cell showing three views and scroll down to a cell only containing 1 view (because the data was only one), then when I scroll up to the first cell again that should originally show three views, it is only showing 1, the first one..
I have an example:
My custom cell in storyboard is like this, the green and black boxes are the different views
This is what it looks like with 6 types of data:
When I then scroll down to a cell containing one view, the cell with 6 views will look like this afterwards:
Here is some relevant code:
Let me explain the code. In my database, every post has a category which is either 1 or 2. That is what the code is searching for, in update.category. If it is category 1, then it is just plain text. If it is category 2 ( or something else ) it should show the views, so I actually have to types of cells in my UITableViewController.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let update = updates[indexPath.row]
if update.category == 1 {
let cell:updateTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! updateTableViewCell
cell.nameButton.setTitle(update.addedByUser, forState: .Normal)
return cell
} else {
let cellSugar:newSugarTableViewCell = tableView.dequeueReusableCellWithIdentifier("CellSugar", forIndexPath: indexPath) as! newSugarTableViewCell
cellSugar.nameButton.setTitle(update.addedByUser, forState: .Normal)
let sugarArray = update.content.componentsSeparatedByString("--")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
cellSugar.sugarViewOne.layer.cornerRadius = cellSugar.sugarViewOne.frame.size.width/2
cellSugar.sugarViewOne.clipsToBounds = true
cellSugar.sugarViewOne.layer.borderColor = UIColor.whiteColor().CGColor
cellSugar.sugarViewOne.layer.borderWidth = 2.0
cellSugar.sugarViewTwo.layer.cornerRadius = cellSugar.sugarViewTwo.frame.size.width/2
cellSugar.sugarViewTwo.clipsToBounds = true
cellSugar.sugarViewTwo.layer.borderColor = UIColor.whiteColor().CGColor
cellSugar.sugarViewTwo.layer.borderWidth = 2.0
})
if sugarArray.count == 1 {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let seperateSugarArray = sugarArray[0].componentsSeparatedByString("#")
if seperateSugarArray[4] == "Candy" {
cellSugar.sugarViewOne.backgroundColor = UIColor(red: 97.0/255.0, green: 194.0/255.0, blue: 231.0/255.0, alpha: 1.0) // Blå
} else if seperateSugarArray[4] == "Ice cream" {
cellSugar.sugarViewOne.backgroundColor = UIColor(red: 35.0/255.0, green: 117.0/255.0, blue: 147.0/255.0, alpha: 1.0) // Mørke grå/blå
} else if seperateSugarArray[4] == "Marshmellows" {
cellSugar.sugarViewOne.backgroundColor = UIColor(red: 75.0/255.0, green: 212.0/255.0, blue: 159.0/255.0, alpha: 1.0) // Tyrkis
}
cellSugar.sugarViewTwo.hidden = true
cellSugar.sugarViewThree.hidden = true
cellSugar.sugarViewFour.hidden = true
cellSugar.sugarViewFive.hidden = true
cellSugar.sugarViewSix.hidden = true
cellSugar.sugarViewSeven.hidden = true
cellSugar.sugarViewEight.hidden = true
cellSugar.sugarViewNine.hidden = true
cellSugar.sugarViewTen.hidden = true
cellSugar.sugarViewEleven.hidden = true
cellSugar.sugarViewTwelve.hidden = true
cellSugar.sugarViewThirteen.hidden = true
cellSugar.sugarViewFourteen.hidden = true
})
} else if sugarArray.count == 2 {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let seperateSugarArray = sugarArray[0].componentsSeparatedByString("#")
let seperateSugarArrayTwo = sugarArray[1].componentsSeparatedByString("#")
if seperateSugarArray[4] == "Candy" {
cellSugar.sugarViewOne.backgroundColor = UIColor(red: 97.0/255.0, green: 194.0/255.0, blue: 231.0/255.0, alpha: 1.0) // Blå
} else if seperateSugarArray[4] == "Ice cream" {
cellSugar.sugarViewOne.backgroundColor = UIColor(red: 35.0/255.0, green: 117.0/255.0, blue: 147.0/255.0, alpha: 1.0) // Mørke grå/blå
} else if seperateSugarArray[4] == "Marshmellows" {
cellSugar.sugarViewOne.backgroundColor = UIColor(red: 75.0/255.0, green: 212.0/255.0, blue: 159.0/255.0, alpha: 1.0) // Tyrkis
}
if seperateSugarArray[4] == "Candy" {
cellSugar.sugarViewTwo.backgroundColor = UIColor(red: 97.0/255.0, green: 194.0/255.0, blue: 231.0/255.0, alpha: 1.0) // Blå
} else if seperateSugarArray[4] == "Ice cream" {
cellSugar.sugarViewTwo.backgroundColor = UIColor(red: 35.0/255.0, green: 117.0/255.0, blue: 147.0/255.0, alpha: 1.0) // Mørke grå/blå
} else if seperateSugarArray[4] == "Marshmellows" {
cellSugar.sugarViewTwo.backgroundColor = UIColor(red: 75.0/255.0, green: 212.0/255.0, blue: 159.0/255.0, alpha: 1.0) // Tyrkis
}
cellSugar.sugarViewThree.hidden = true
cellSugar.sugarViewFour.hidden = true
cellSugar.sugarViewFive.hidden = true
cellSugar.sugarViewSix.hidden = true
cellSugar.sugarViewSeven.hidden = true
cellSugar.sugarViewEight.hidden = true
cellSugar.sugarViewNine.hidden = true
cellSugar.sugarViewTen.hidden = true
cellSugar.sugarViewEleven.hidden = true
cellSugar.sugarViewTwelve.hidden = true
cellSugar.sugarViewThirteen.hidden = true
cellSugar.sugarViewFourteen.hidden = true
})
}
return cellSugar
}
}
I hope you understand and that you can help me, as it is pretty annoying :-)
You have to update all elements for each condition.
the cause of your issue: the table view is reusing the cells so if you have a cell showing 5 labels and after scrolling you have another one has a 3 and the other 2 is hidden, when you scroll up again you have to make them visible again a table view is using the one with 2 hidden labels.
In CellForRowAtIndexPath add the missing labels in each conditions
or in your custom UItableViewCell class add the method prepareForReuseand make all the labels visible.
UITableView reuse cells in a way that it takes one cell in its current state (with all its views configured) and return that cell for an other row, for which you need to again configure the cell as you need.
if update.category == 1 {
let cell:updateTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! updateTableViewCell
cell.nameButton.setTitle(update.addedByUser, forState: .Normal)
return cell
}
You need to configure your cell for a current row data before returning it.

Resources