observing contentSize (CGSize) with KVO in swift
Asked Answered
T

4

10

I'm trying to observering collectionView.contentSize like this :

func startObserveCollectionView() {
    collectionView.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.Old.union(NSKeyValueObservingOptions.New), context: &SearchDasboardLabelContext)
}

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
    if context == &SearchDasboardLabelContext {       
        if object === collectionView && keyPath! == "contentSize" {
            print(change)               
        }
    }
}

and in xcode terminal I got a NSSize not CGSize like this :

Optional(["old": NSSize: {320, 0}, "new": NSSize: {375, 39.5}, "kind": 1])

In objective-c I used method CGSizeValue

CGSize newContentSize = [[change objectForKey:NSKeyValueChangeNewKey] CGSizeValue];

Is there any method like CGSizeValue in swift

I have tried in swift var newContentSize = change[NSKeyValueChangeNewKey]?.CGSizeValue() but got error

could not find member 'CGSizeValue'

need help anyone? Thanks

Topless answered 15/7, 2015 at 4:14 Comment(0)
C
3

Are you on iOS? Because I am, I did the same thing and arrived at the same question; why NSSize? Maybe that's just the xcode terminal playing a trick on us.

Anyway, you can cast it to an NSValue then you will be able to use CGSizeValue:

if let zeChange = change as? [NSString: NSValue] {
    let oldSize = zeChange[NSKeyValueChangeOldKey]?.CGSizeValue()
    let newSize = zeChange[NSKeyValueChangeNewKey]?.CGSizeValue()
}
Caecilian answered 26/8, 2015 at 22:32 Comment(0)
G
14

With Swift 4, you can cast the result of the change dictionary for the key NSKeyValueChangeKey.newKey as being of type CGSize:

if let size = change?[NSKeyValueChangeKey.newKey] as? CGSize {
    /* ... */
}

The following UIViewController implementation shows how to set a KVO stack in order to observe the changes of the contentSize property of any UIScrollView subclass (e.g UITextView):

import UIKit

private var myContext = 0

class ViewController: UIViewController {

    @IBOutlet weak var textView: UITextView!

    /* ... */

    override func viewDidLoad() {
        super.viewDidLoad()

        textView.addObserver(self, forKeyPath: #keyPath(UITextView.contentSize), options: [NSKeyValueObservingOptions.new], context: &myContext)
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if context == &myContext,
            keyPath == #keyPath(UITextView.contentSize),
            let contentSize = change?[NSKeyValueChangeKey.newKey] as? CGSize {
            print("contentSize:", contentSize)
        }
    }

    deinit {
        textView.removeObserver(self, forKeyPath: #keyPath(UITextView.contentSize))
    }

}

Note that with Swift 4, as an alternative to addObserver(_:, forKeyPath:, options:, context:) and observeValue(forKeyPath:, of:, change:, context:), you can use observe(_:, options:, changeHandler:) in order to track your UIScrollView subclass contentSize property changes:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var textView: UITextView!
    var observer: NSKeyValueObservation?

    /* ... */

    override func viewDidLoad() {
        super.viewDidLoad()

        let handler = { (textView: UITextView, change: NSKeyValueObservedChange<CGSize>) in
            if let contentSize = change.newValue {
                print("contentSize:", contentSize)
            }
        }
        observer = textView.observe(\UITextView.contentSize, options: [NSKeyValueObservingOptions.new], changeHandler: handler)
    }

}
Gap answered 10/8, 2017 at 20:21 Comment(2)
Regarding the Swift 4 code: is this supposed to get called when the scrollview scrolls? I am getting multiple successive outputs with the same contentSize every time I scroll. I would think this should only trigger if the actual contentSize changesHemidemisemiquaver
What for is the "\" in ... observe(\UITextView.contentSize ...?Hog
C
3

Are you on iOS? Because I am, I did the same thing and arrived at the same question; why NSSize? Maybe that's just the xcode terminal playing a trick on us.

Anyway, you can cast it to an NSValue then you will be able to use CGSizeValue:

if let zeChange = change as? [NSString: NSValue] {
    let oldSize = zeChange[NSKeyValueChangeOldKey]?.CGSizeValue()
    let newSize = zeChange[NSKeyValueChangeNewKey]?.CGSizeValue()
}
Caecilian answered 26/8, 2015 at 22:32 Comment(0)
I
1

There's a simpler and arguably swiftier alternative.

You can subclass UICollectionViewLayout (or any of its subclasses, like UICollectionViewFlowLayout) and override a computed property collectionViewContentSize. By calling super you'll get the contentSize of your collection and be able to delegate this value back to your code.

So you'll have something like this:

protocol FlowLayoutDelegate: class {
    func collectionView(_ collectionView: UICollectionView?, didChange contentSize: CGSize)
}

class FlowLayout: UICollectionViewFlowLayout {

    weak var delegate: FlowLayoutDelegate?

    override var collectionViewContentSize: CGSize {
        let contentSize = super.collectionViewContentSize
        delegate?.collectionView(collectionView, didChange: contentSize)
        return contentSize
    }

}
Interurban answered 26/4, 2019 at 10:8 Comment(0)
J
0

Check out this example code:

if context == ApprovalObservingContext{
        if let theChange = change as? [NSString: Bool]{

            var newContentSize = change[NSKeyValueChangeNewKey]?.CGSizeValue()
        }
    }

This is not giving any error.

Jackiejackinoffice answered 15/7, 2015 at 5:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.