1

I'm trying to change the behavior of a variable within a class.

To be more precise, I have a 'UIWebView' which has a 'scrollview' property.

I need to override the 'setContentInset' of this scrollview, however I'm not sure how to do this.

The docs show me the UIWebView has this:

var scrollView: UIScrollView { get }

So I can't simply create my own and set it there.

My question is, how do I override the 'setContentInset' from the UIWebView's scrollview?

Edit:

2 clarifications.

I know the UIWebView is NOT supposed to be subclassed, as per the documentation's subclassing notes.

My goal is to fix the "pull down to refresh" jumpy behavior following this answer that I already implemented on my app's other scrollviews.

Community
  • 1
  • 1
Pochi
  • 13,391
  • 3
  • 64
  • 104
  • I don't know if you can achieve what you want, but as a workaround consider extending the `UIScrollViewDelegate` protocol to add a delegate method `scrollViewDidSetContentInset(UIScrollView)`, then extend `UIScrollView` to call it whenever `contentInset` is set. – Robert Dec 28 '16 at 02:28

2 Answers2

0

When a built-in interface class "has" another built-in interface class, you cannot subclass the inner class or override anything about it.

Thus, although you can subclass UIWebView, you cannot somehow subclass a UIWebView's scroll view.

Another familiar example is UIButton. Everyone knows that UIButton contains a UILabel and a UIImageView. You can subclass UIButton but you can't subclass its UILabel or UIImageView.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • It would be tricky to override `titleLabel` and `imageView` of `UIButton`, but that's because of the different button states. I have successfully overridden both `textLabel` and `imageView` in subclasses of `UITableViewCell`. What problems would you expect when overriding `scrollView`? – Jeffery Thomas Dec 28 '16 at 03:44
0

I would start with a straight forward approach, and see if there are any subtle problems.

class MyScrollView: UIScrollView {
    private lazy var my_contentInset: UIEdgeInsets = UIEdgeInsets()
    override var contentInset: UIEdgeInsets {
        get { return my_contentInset }
        set {
            // Your willSet code
            my_contentInset = newValue
            // Your didSet code
        }
    }
}

class MyWebView: UIWebView {
    private lazy var my_scrollView: MyScrollView = MyScrollView()
    override var scrollView: UIScrollView {
        get { return my_scrollView }
    }
}

This is a bit simplistic, so you may run into problems depending how the runtime sets scrollView. If the runtime expects a setter for scrollView, then you'll need to get deeper.

Here I create a initializer for my scroll view which clones a UIScrollView and uses that in the setter for MyWebView's scrollView.

extension MyScrollView {
    convenience init(scrollView: UIScrollView) {
        let data = NSKeyedArchiver.archivedData(withRootObject: scrollView)
        let coder = NSKeyedUnarchiver(forReadingWith: data)
        self.init(coder: coder)! // Intentionally failing hard on decode failure.

        // Additional configuration not captured in the encode/decode
        // process might be needed.
    }
}

class MyWebView: UIWebView {
    private lazy var my_scrollView: MyScrollView = MyScrollView()
    override var scrollView: UIScrollView {
        get { return my_scrollView }
        set { my_scrollView = MyScrollView(scrollView: newValue) }
    }
}

Even this might not be enough. The runtime can cheat in lots of ways when dealing with internal objects. It should be enough to get you started.

Jeffery Thomas
  • 42,202
  • 8
  • 92
  • 117