Having trouble passing property into drawRect in UIView - ios

Not understanding why my property resets to the original value assigned (0.1). I pass in a fillHeight of 0.5 from outside method. The property is set in the convenience init, but does not carry over to the drawRect. What am I missing?
import UIKit
class MyView: UIView {
var fillHeight: CGFloat = 0.1
override init(frame: CGRect) {
super.init(frame: frame)
}
convenience init(fillHeight: CGFloat) {
self.init()
self.fillHeight = fillHeight
print("self.fillHeight: \(self.fillHeight) and fillHeight: \(fillHeight)")
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
override func drawRect(rect: CGRect) {
print("drawRect self.fillHeight: \(self.fillHeight)")
// custom stuff
}
}
Output on the console:
outsideAmount:Optional(0.5)
self.fillHeight: 0.5 and fillHeight: 0.5
drawRect self.fillHeight: 0.1
EDIT:
The outside call comes from UITableViewController with a custom UITableViewCell. The image is for the cell.
func configureCell(cell: CustomTableViewCell, atIndexPath indexPath: NSIndexPath) {
let myObject = self.fetchedResultsController.objectAtIndexPath(indexPath) as! MyObject
cell.nameLabel.text = myObject.name
cell.strengthLabel.text = myObject.strength
cell.myView = MyView(fillHeight: CGFloat(myObject.fillAmount!))
...
MORE EDIT:
import UIKit
class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var strengthLabel: UILabel!
#IBOutlet weak var myView: MyView!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

The problem is that you assign a new MyView instance whenever you configure your cell. You don't have to do that because the view is already there (because you added it in the nib).
So just set fillHeight on the cell's myView. That fixes the problem:
func configureCell(cell: CustomTableViewCell, atIndexPath indexPath: NSIndexPath) {
let myObject = self.fetchedResultsController.objectAtIndexPath(indexPath) as! MyObject
cell.nameLabel.text = myObject.name
cell.strengthLabel.text = myObject.strength
cell.myView.fillHeight = CGFloat(myObject.fillAmount!)
....
}

Related

How to set customView in UITableViewCell with Swift

I have UITableView, UITablaViewCell, CustomView
and UITableViewCell includes customView.
I'm trying to put Product's data to cell with my function not cellForRowAt.
Cell shows just origin view ProductView.xib with empty data
Please help.
ViewController.swift
struct Product {
let brand: String
let product: String
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath) as! ProductTableViewCell
// this line is not work
// productView is not update
cell.productView = createProductView(product: product)
return cell
}
func createProductView(product: Product) -> ProductView {
let productView = ProductView()
productView.brandLabel.text = product.brand
productView.productLabel.text = product.product
return productView
}
UITableViewCell.swift
class ProductTableViewCell: UITableViewCell {
#IBOutlet var productView: ProductView!
}
ProductView.swift
class ProductView: UIView {
#IBOutlet weak var productView: UIView!
#IBOutlet weak var brandLabel: UILabel!
#IBOutlet weak var productLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit(){
let bundle = Bundle(for: ProductView.self)
bundle.loadNibNamed("ProductView", owner: self, options: nil)
addSubview(productView)
productView.frame = self.bounds
}
There are multiple issues with your code
Issue 1: cellForRowAt IndexPath gets called multiple times, with your code you will end up creating a new ProductView every time tableView is scrolled (cell is reused). instead you can create product view only once and update its label every time cell is reused
Issue 2: In ProductView's commonInit you set the frame using productView.frame = self.bounds self.bounds will always be(0,0,0,0). Because you have instantiated ProductView as ProductView()
Issue 3: createProductView is supposed to return an instance of ProductView hence the method signature is invalid so you should change it from func createProductView(product: Product) -> ProductView() to func createProductView(product: Product) -> ProductView as already suggested in answer above
What can be better solution?
class ProductTableViewCell: UITableViewCell {
var productView: ProductView!
func updateProductView(product: Product) {
productView.brandLabel.text = product.brand
productView.productLabel.text = product.product
}
override func prepareForReuse() {
super.prepareForReuse()
productView.brandLabel.text = nil
productView.productLabel.text = nil
}
override func awakeFromNib() {
super.awakeFromNib()
self.productView = createProductView()
self.addSubview(self.productView)
self.productView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
self.productView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
self.productView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
self.productView.topAnchor.constraint(equalTo: self.topAnchor),
self.productView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
])
}
func createProductView() -> ProductView {
return ProductView()
}
}
Finally in your cellForRowAtIndexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "tableViewCell", for: indexPath) as! ProductTableViewCell
//assuming you already have a product for index path
cell.updateProductView(product: product)
return cell
}
EDIT:
As OP is facing issue with loading nib from bundle updating the answer here.
In your ProductView common init change the way you access bundle from
Bundle(for: ProductView.self) to Bundle.main as shown below
class ProductView: UIView {
#IBOutlet weak var productView: UIView!
#IBOutlet weak var brandLabel: UILabel!
#IBOutlet weak var productLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit(){
Bundle.main.loadNibNamed("ProductView", owner: self, options: nil)
addSubview(productView)
}
Few things to take care
Ensure you have an XIB named ProductView in your bundle
Ensure you have set its file owner to ProductView

NSUnknownKeyException when using an IBOutlet in UICollectionViewCell in .xib

I've been trying to add a label using an IBOutlet from an xib to a custom UICollectionViewCell class that is meant to be used as the cells for a UICollectionView that I have in another xib but when I add the outlet I get an NSUnknownKeyException on the label reference I've created, without the outlet reference the contents of the cell load properly but I want to be able to manipulate the label within the cell.
Heres what I have in my parent xib class:
class Calendar : UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
let nibName: String = "Calendar"
let calendarDayNibName: String = "CalendarDay"
let calendarReusableCellName: String = "CalendarCell"
let calendarDaysLimit: Int = 35
var contentView: UIView?
#IBOutlet var sundayLabel: UILabel!
#IBOutlet var mondayLabel: UILabel!
#IBOutlet var tuesdayLabel: UILabel!
#IBOutlet var wednesdayLabel: UILabel!
#IBOutlet var thursdayLabel: UILabel!
#IBOutlet var fridayLabel: UILabel!
#IBOutlet var saturdayLabel: UILabel!
#IBOutlet var calendarDays: UICollectionView!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
guard let view = self.loadViewFromNib() else { return }
view.frame = self.bounds
self.addSubview(view)
contentView = view
contentView?.isUserInteractionEnabled = true
}
override func awakeFromNib()
{
super.awakeFromNib()
calendarDays.register(UINib(nibName: calendarDayNibName, bundle: nil), forCellWithReuseIdentifier:calendarReusableCellName)
calendarDays.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return calendarDaysLimit;
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: calendarReusableCellName, for: indexPath)
configureCell(cell: cell)
return cell
}
private func configureCell(cell: UICollectionViewCell)
{
//does nothing right now, placeholder for if configuring cell on
//collection view load
}
private func loadViewFromNib() -> UIView? {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: nibName, bundle: bundle)
self.isUserInteractionEnabled = true
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
public func loadCalendarDays(month: Int)
{
//todo: write day loading logic here
}
}
Here is the child xib class (UICollectionViewCell):
class CalendarDay: UICollectionViewCell
{
#IBOutlet weak var dayLabel: UILabel!
}
Here is my overall project if it helps to look at: https://github.com/CharlemagneVI/practice-calendar
You've set the classes and IBOutlet connections wrong... well, not-quite-right...
In CalendarDay.xib, the class of File's Owner should be the default NSObject:
and it should not have any connections:
The class of the cell object itself should be CalendarDay:
and that is where you make your connection:
That should do it :)

didSelectRowAtIndexPath not getting called when row is pressed

I have 5 custom cells in a table view but didSelectRowAtIndexPath is not working for only one cell. I have added the cell for the row at the index for that cell in the code.
let cell : FileSentCell = chatTableView.dequeueReusableCell(withIdentifier: "fileSend") as! FileSentCell
let fileUrl = self.getImageURL(forFileName: fileNameString!)
if(fileUrl == nil){
cell.downloadStatusIndicator.image = #imageLiteral(resourceName: "fileDownloadWhite")
}else{
cell.downloadStatusIndicator.image = #imageLiteral(resourceName: "downloadedWhite")
}
cell.downloadStatusIndicator.isHidden = false
cell.downloadingIndicator.alpha = 0.0
cell.sendFileView.layer.cornerRadius = 10
cell.name.text = groupMsg.senderDisplayName
cell.message.text = groupMsg.message
cell.time.text = groupMsg.dateSent!
cell.fileStatus.text = groupMsg.status
cell.fileIcon.image = returnImage(fileName:groupMsg.message!)
cell.sendFileView.tag = indexPath.row
cell.sendFileView.addGestureRecognizer(longPressGesture)
cell.sendFileView.isUserInteractionEnabled = true
return cell
I have made UITableview have a single selection. and didSelectRowAtIndexPath is getting called for all the other reuse cells except for this one cell
Is there a reason or should I change some thing in the cellforat row
My FileSentCell class is
import UIKit
import QuartzCore
class FileSentCell: UITableViewCell {
#IBOutlet var sendFileView : UIView!
#IBOutlet var message : ChatLabel!
#IBOutlet var time : UILabel!
#IBOutlet var fileStatus : UILabel!
#IBOutlet var fileIcon : UIImageView!
#IBOutlet var downloadStatusIndicator : UIImageView!
#IBOutlet var downloadingIndicator: UIActivityIndicatorView!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init(coder aDecoder: NSCoder) {
//fatalError("init(coder:) has not been implemented")
super.init(coder: aDecoder)!
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
override func layoutSubviews() {
super.layoutSubviews()
}
}
One more thing is if I reduce the width of UIView inside content view and click the row where UIView is not being displayed didSelectRow is getting called, So didSelectRow is not getting called only when it is clicked on the UIView inside content View
Thanks in Advance.
Set:
cell.contentView.isUserInteractionEnabled = true

FirebaseUi - Cast value of UITableViewCell to custom class in swift

I can t seem to get the prototypereuseidentifier thing on firebaseUI. If I add a prototypecell, I can only give it a cellidentifier
In my following code, my IBoutlets linked to my custom cell return nil.
here s my code :
func loadData() {
self.dataSource = FirebaseTableViewDataSource(ref: refpost, cellClass: MainWitnessTableViewCell.self, cellReuseIdentifier: "<RIcell>", view: self.tableView)
self.dataSource.populateCellWithBlock { (cell: UITableViewCell, obj: NSObject) -> Void in
let snap = obj as! FDataSnapshot
print(cell)
let mycell = cell as! MainWitnessTableViewCell
let keyy: String = (snap.value.objectForKey("author") as? String)!
mycell.postContent.text = keyy
print (mycell.postContent.text)
}
self.tableView.dataSource = self.dataSource
}
here, mycell.postContent.text returns nil, is there any sorcery that keeps blinding me ? :)
sincerely
Yann
I think you should change
cellReuseIdentifier: "<RIcell>"
to
cellReuseIdentifier: "RIcell"
# jonas : here s the cell class
class MainWitnessTableViewCell: UITableViewCell, UITextFieldDelegate {
#IBOutlet weak var cellImage: UIImageView!
#IBOutlet weak var postOwner: UILabel!
#IBOutlet weak var postDate: UILabel!
#IBOutlet weak var postContent: UITextView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
postContent.text = ""
postOwner.text = ""
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

Thread 1: EXC_BAD_INSTRUCTION Error

Hi I've been trying to code a UITableView Custom Cell and when I ran it I keep getting the error "Thread 1: EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0)" on this line of code:
let cell: CustomCell = tableView.dequeueReusableCellWithIdentifier("Cell") as CustomCell
My full code is below
In ViewController.swift:
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var mytableview: UITableView!
var arrayOfPersons: [Person] = [Person]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.setUpPersons()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func setUpPersons()
{
var person1 = Person(name: "Anna", number: 60, imageName: "testingimage.jpg")
var person2 = Person(name: "Joes", number: 10, imageName: "testingimage2.jpg")
arrayOfPersons.append(person1)
arrayOfPersons.append(person2)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return arrayOfPersons.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: CustomCell = tableView.dequeueReusableCellWithIdentifier("Cell") as CustomCell
if indexPath.row % 2 == 0
{
cell.backgroundColor = UIColor.darkGrayColor()
}
else
{
cell.backgroundColor = UIColor.blackColor()
}
let person = arrayOfPersons [indexPath.row]
cell.setCell(person.name, rightlabelInt: person.number, imageName: person.imageName)
return cell
}
}
In CustomCell.Swift:
import UIKit
class CustomCell: UITableViewCell {
#IBOutlet weak var leftlabel: UILabel!
#IBOutlet weak var rightlabel: UILabel!
#IBOutlet weak var myimageview: UIImageView!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) }
override init(style: UITableViewCellStyle, reuseIdentifier: String?){
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
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(leftlabelText: String, rightlabelInt: Int, imageName: String)
{
self.leftlabel.text = leftlabelText
self.rightlabel.text = String(rightlabelInt)
self.myimageview.image = UIImage(named: imageName)
}
}
And in Person.Swift:
import Foundation
class Person {
var name = "name"
var number = 0
var imageName = "blank"
init(name: String, number: Int, imageName: String)
{
self.name = name
self.number = number
self.imageName = imageName
}
}
I am not sure what the error means or if I made a mistake somewhere. Any help would be great!
Thanks!
I had a similar problem with a class inheriting from UITextView. The solution turned out to be I needed to override all the init methods.
override init()
{ super.init() }
override init(frame: CGRect, textContainer: NSTextContainer)
{ super.init(frame: frame, textContainer: textContainer) }
override init(frame: CGRect)
{ super.init(frame: frame) }
required init(coder aCoder: NSCoder)
{ super.init(coder: aCoder) }

Resources