I have a custom subclass of UITableViewCell that, in its initializer, loads a nib with itself as the File's Owner, and has outlets to the nib's objects (one of which is a UITextView). But when I set the following property, it doesn't seem to be receiving the message because the textView is still editable at runtime.
self.textView.isEditable = false
However, it does seem to be receiving other messages. In a subclass I set the attributed text for the textView and here I set the tintColor, both of which affect the behavior. I have attached the subclass and the nib below.
class BannerCell: UITableViewCell, NonHighlightableCell, UITextViewDelegate {
@IBOutlet var view: UIView!
@IBOutlet var closeButton: UIButton!
@IBOutlet var textView: UITextView!
var delegate: BannerCellDelegate!
var destination: BannerCell.Destination?
// Paragraph styling
// I'm assuming this will be the same for all banners in our new design system, but if not we can change to an instance variable
static var paragraphAttributes: [NSAttributedString.Key: Any] {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineHeightMultiple = 1.5
return [
.foregroundColor: UIColor.black,
.font: UIFont(name: "Proxima Nova", size: 16.0)!,
.paragraphStyle: paragraphStyle
]
}
// Link text styling
// Same assumption as above
static let linkTextAttributes: [NSAttributedString.Key: Any] = [
.underlineStyle: NSUnderlineStyle.single.rawValue,
.foregroundColor: BSCConstants.bscLinkColor()!,
.link: "https://www.dummy.com" // This is a dummy URL - it just makes the link text tappable. The action itself is handled by the UITextViewDelegate method textView(_:shouldInteractWith:in:interaction:)
]
//MARK: Initializers
//Designated Initializer
required init(delegate: BannerCellDelegate, style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
//Set the delegate for this banner
self.delegate = delegate
// Instantiate Nib
let nib = UINib(nibName: "BannerCell", bundle: Bundle.main)
// Load outlets to self
nib.instantiate(withOwner: self, options: nil)
// Add view (from Nib) as subview of self's contentView
self.contentView.addSubview(self.view)
// Disable auto-resizing
self.view.translatesAutoresizingMaskIntoConstraints = false
// Set constraints within cell's contentView
self.view.topAnchor.constraint(equalTo: self.contentView.topAnchor).isActive = true
self.view.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor).isActive = true
self.view.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor).isActive = true
self.view.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor).isActive = true
// Sets the textView delegate
self.textView.delegate = self
// Allows the link to be tapped, configures appearance
self.textView.isEditable = false
self.textView.isSelectable = false
self.textView.isScrollEnabled = false
self.textView.tintColor = UIColor.clear
// Removes default padding and sets text container inset
self.textView.textContainer.lineFragmentPadding = 0.0
self.textView.textContainerInset = UIEdgeInsets(top: -4.0, left: 0, bottom: 4, right: 0)
// Removes highlight when cell is selected
noSelectionStyle()
// Accessibility traits
self.closeButton.accessibilityIdentifier = "closeButton"
self.closeButton.accessibilityLabel = "Close"
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
//MARK: Actions
@IBAction func closeButtonPressed(_ sender: UIButton) {
self.delegate.dismissBanner(self.tag)
}
//MARK: NonHighlightableCell
func noSelectionStyle() {
self.selectionStyle = .none
}
//MARK: UITextViewDelegate
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
guard let destination = self.destination else { return false }
self.delegate.navigateTo(destination: destination)
return false
}
}
At first I thought this could be an issue with the UIResponder chain, but I checked the view hierarchy debugger and everything seems to be in the right order (with the nib's view being nested within the cell's contentView). I suppose it still could be but I can't seem to find anyone else with this same issue. I saw someone with a similar issue (but not the same one) mention that setting UITableViewCell.isUserInteractionEnabled to false worked for them, but for me it simply disabled all user interaction within the cell, which is not what I want (I have a button and a link in there).
I'm anticipating being asked why I need this design of loading the nib to self and configuring the outlets within self as opposed to setting file's owner to some retaining class (the view controller I suppose) and configuring this object there instead. Well, there is never going to be more than one instance of this cell. So it doesn't need to be configured by some datasource or anything.
Anyways, any guidance would be much appreciated.
UPDATE:
I tried a different design where I set the File's Owner to be the view controller with the table view, not the cell subclass itself. I put the initialization code in awakeFromNib() but it still didn't receive the message with regards to the property above. Then, I set the property in tableview(_:cellForRowAt:) and it worked. Now my question becomes: Why does it only work when you set this property there and not any of the other places I've tried?