2

I am subclassing UITextView and implementing some delegate methods in subclass like textViewDidChangeSelection but I also need to get notify in View Controller for UITextView delegates. So if I create object of subclass and set textview delegate in view controller then delegates method is notified only in view controller not inside subclass. I need to notify both class. And language I am using is swift 2

I tried to inherit UITextViewDelegate in subclass delegate:

@objc protocol CustomTextViewDelegate:UITextViewDelegate {

    func customTextViewDidChangeSize(chatTextView: CustomTextView)

}

and then in VC:

let customTV = CustomTextView()
customTV.customTextViewDelegate = self

but any textview delegate method is not getting called.

Sunil Sharma
  • 2,586
  • 1
  • 22
  • 34
  • You can make your custom text view delete, and set it from `UIViewController` and when your textview tells itself about `textViewDidChangeSelection` you would tell your `UIViewController` through you custom delegate. – Adil Soomro Mar 26 '16 at 21:05
  • There are lot other methods in textview delegate and I don't want to do this for all.Is there any way that I can make to listen both subclass and VC. – Sunil Sharma Mar 26 '16 at 21:10
  • You can set the delegate in custom text view and then in `ViewController` like this: `[texView addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];` More info [here](http://stackoverflow.com/a/7010676/593709) – Adil Soomro Mar 26 '16 at 21:19
  • addTarget method is not available for textview. – Sunil Sharma Mar 26 '16 at 21:25
  • My bad, I guess the only way is to implement custom delegate liket his user did. [UITextView delegate methods](http://stackoverflow.com/a/25318852/593709) – Adil Soomro Mar 26 '16 at 21:27

3 Answers3

2

Good question. Here is a not so good answer, since it requires you to rewrite all the delegate-methods and is therefore not stable across iOS-versions in case the delegate methods change over time.

In this approach the ViewController and the CustomTextField both have access to delegate-events.

class CustomTextView: UITextView {
   override var delegate: UITextViewDelegate? {
      set {
         superDelegate = newValue
      } get {
         return superDelegate
      }
    }

    private weak var superDelegate: UITextViewDelegate?

    init() {
       super.init(frame: .zero, textContainer: nil)
       super.delegate = self
    }

    func textDidChange(text: String?) {
        // do something
    }

}

extension BoundTextView: UITextViewDelegate {
    public func textViewDidChange(_ textView: UITextView) {
        // catch text-change events here
        textDidChange(text: String?) 
        superDelegate?.textViewDidChange?(textView)
    }

    public func textViewDidEndEditing(_ textView: UITextView) {
        superDelegate?.textViewDidEndEditing?(textView)
    }

    public func textViewDidChangeSelection(_ textView: UITextView) {
        superDelegate?.textViewDidChange?(textView)
    }

    public func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
       return superDelegate?.textViewShouldBeginEditing?(textView) ?? false
    }

    public func textViewDidBeginEditing(_ textView: UITextView) {
        superDelegate?.textViewDidBeginEditing?(textView)
    }

    public func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
       return superDelegate?.textViewShouldEndEditing?(textView) ?? false
    }

    public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        return superDelegate?.textView?(textView, shouldChangeTextIn: range, replacementText: text) ?? false
    }

    public func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        return superDelegate?.textView?(textView, shouldInteractWith: URL, in: characterRange, interaction: interaction) ?? false
    }

    public func textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        return superDelegate?.textView?(textView, shouldInteractWith: textAttachment, in: characterRange, interaction: interaction) ?? false
    }
}

We override the delegate and store a reference to it in a separate variable (called superDelegate). The CustomTextField assigns itself to super.delegate and implements the UITextView-delegate. We have to make sure that every delegate-event triggers the corresponding superDelegate's event.

Our 'ViewController' can now assign itself as the CustomTextView's delegate:

class ViewController: UIViewController {

   ...
   lazy var textField: CustomTextView {
      let textView = CustomTextField()
      textView.delegate = self 
      return textField
   }()
   ...


}

extension ViewController: UITextViewDelegate {

   // implement only the delegate-methods you need

}

Now both, the ViewController and the CustomTextField, have both access to the UITextFieldDelegate.

nayooti
  • 356
  • 2
  • 13
0

Two objects can't be delegate to the UITextView object at the same time. For this reason you should create new protocol (CustomTextViewDelegate) for your CustomTextView and create delegate property in it. Make your ViewController confirm this CustomTextViewDelegate and implement it's methods. Inside your CustomTextView's implementation of UITextViewDelegate methods you can call appropriate CustomTextViewDelegate methods.

Shamsiddin Saidov
  • 2,223
  • 4
  • 22
  • 35
0

@nayooti 's approach is excellent. However I have to do a few fixes in order to work properly. The main one being that some delegates methods won't get called (E.g. shouldChangeTextIn range and hence textViewDidChange as well).

I found out it can be fixed by returning super.delegate instead of superDelegate on the get accessor of the overridden delegate variable.

class CustomTextView: UITextView {
    
    override var delegate: UITextViewDelegate? {
        set {
            superDelegate = newValue
        } get {
            return super.delegate
        }
    }
    
    private weak var superDelegate: UITextViewDelegate?
    
    init() {
        super.init(frame: .zero, textContainer: nil)
        super.delegate = self
    }
    
    override public init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        super.delegate = self
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        super.delegate = self
    }
    
    func textDidChange(text: String?) {
        // Do something
    }
    
}

extension CustomTextView: UITextViewDelegate {
    
    public func textViewDidChange(_ textView: UITextView) {
        // Catch text-change events here
        textDidChange(text: textView.text)
        superDelegate?.textViewDidChange?(textView)
    }
    
    public func textViewDidEndEditing(_ textView: UITextView) {
        superDelegate?.textViewDidEndEditing?(textView)
    }
    
    public func textViewDidChangeSelection(_ textView: UITextView) {
        superDelegate?.textViewDidChange?(textView)
    }
    
    public func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
        return superDelegate?.textViewShouldBeginEditing?(textView) ?? true
    }
    
    public func textViewDidBeginEditing(_ textView: UITextView) {
        superDelegate?.textViewDidBeginEditing?(textView)
    }
    
    public func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
        return superDelegate?.textViewShouldEndEditing?(textView) ?? true
    }
    
    public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        return superDelegate?.textView?(textView, shouldChangeTextIn: range, replacementText: text) ?? true
    }
    
    public func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        return superDelegate?.textView?(textView, shouldInteractWith: URL, in: characterRange, interaction: interaction) ?? true
    }
    
    public func textView(_ textView: UITextView, shouldInteractWith textAttachment: NSTextAttachment, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        return superDelegate?.textView?(textView, shouldInteractWith: textAttachment, in: characterRange, interaction: interaction) ?? true
    }
    
}
odm
  • 860
  • 1
  • 14
  • 22