UITextView within custom table view cell responds to some messages, not others

50 Views Asked by At

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
    }
    
}

nib file

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?

0

There are 0 best solutions below