How to observe Image rotation? - ios

I have an ImageView called - (woowwww): ImageView
#IBOutlet weak var ImageView: UIImageView!
... and I'd like to fire an event every time the transform-property of the ImageView changed.
An event should get called every time the ImageView get's rotated by maybe something like this:
self.ImageView.transform = CGAffineTransform(rotationAngle: CGFloat(10))
To achieve this I tried to use some kind of KVO but obviously it is not working as expected...
override func viewDidLoad() {
super.viewDidLoad()
self.ImageView.addObserver(self.ImageView.transform, forKeyPath: "test", options: .new, context: nil)
}
func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutableRawPointer) {
print("changed")
}
Any help would be very appreciated! Thanks.

Set "transform" in keyPath like below:
imgView.addObserver(self, forKeyPath: "transform", options: .new, context: nil)
then add below code:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let newValue = change?[.newKey] as? NSObject {
print("Changed \(newValue)")
}
Hope it will help you.

keyPath should be nameOfObject.property so if imageView name is dd to listen to transform it's should dd.transform
Try this
class ViewController: UIViewController {
#IBOutlet weak var dd: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.addObserver(self, forKeyPath: "dd.transform", options: .new, context: nil)
self.dd.transform = CGAffineTransform(rotationAngle: CGFloat(10))
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let newValue = change?[.newKey] as? NSObject {
print("Changed \(newValue)")
}
}}

Related

Observe a property from a viewController

I try to observe a property in a view controller, for example, isBeingDimissed property.. But It doesnt seem to work... Only when I set the options to .initial do I see that it works.
But I wanna observe new values.. Am I missing anything?
class ViewController: UIViewController {
var firstViewController = FirstViewController.init()
override func viewDidLoad() {
super.viewDidLoad()
firstViewController.addObserver(self, forKeyPath: #keyPath(UIViewController.isBeingPresented), options: .new, context: &myContext)
}
#IBAction func tapButton(_ sender: Any) {
present(firstViewController, animated: true, completion: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("let me see the change:", keyPath)
}
}

object communication when object is not kvc compliant in Swift

I want to create a custom view that display it when an action occurs (like changed property) on another object (subclass of UIControl)
My approach was it
create a protocol whose UIControl objects can conform
create my custom view in which I can observe my delegate
Unfortunately, it's not work, worse, it's crash because compiler say "its not kvc-compliant"
Bellow, my code :
protocol CustomDelegate: class where Self : UIControl {
func respondeToControl() -> Bool
}
class CustomView: UIView {
weak var delegate: CustomDelegate? {
didSet{
observeIfControlIsPressed()
}
}
func observeIfControlIsPressed(){
if delegate != nil {
let keypath = delegate! as! UIControl
keypath.addObserver(keypath,
forKeyPath: "backgroundColor",
options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old],
context: nil)
}
}
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("background changed")
}
}
My question is, how I can re-design my solution to make it
work ?
You addObserver is wrong, the "observer" should be self no "keypath"
Try this:
keypath.addObserver(self, forKeyPath: "backgroundColor", options: [.new, .old], context: nil)
You did not read your error correctly.. For example:
protocol CustomDelegate : class where Self : UIControl {
func respondeToControl() -> Bool
}
class CustomView: UIView {
weak var delegate: CustomDelegate? {
didSet{
observeIfControlIsPressed()
}
}
func observeIfControlIsPressed(){
if delegate != nil {
let keypath = delegate! as! UIControl
keypath.addObserver(keypath,
forKeyPath: "backgroundColor",
options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old],
context: nil)
}
}
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("background changed")
}
}
class Foo : UIControl, CustomDelegate {
func respondeToControl() -> Bool {
return true
}
}
class ViewController: UIViewController {
let testBlock = UIView()
override func viewDidLoad() {
super.viewDidLoad()
let view = CustomView()
let button = Foo()
view.delegate = button
button.backgroundColor = UIColor.red
}
}
Throws an exception:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<Meh.Foo: 0x7fc977c1a0c0; baseClass = UIControl; frame = (0 0; 0 0); layer = <CALayer: 0x608000226480>>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: backgroundColor
It means Foo is observing foo but did not implement the observeValueForKeyPath function..
Now why is that? Well.. it's because you did:
keypath.addObserver(keypath, //keypath is observing itself..
forKeyPath: "backgroundColor",
options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old],
context: nil)
where keypath is your delegate.. and is adding observer on itself..
It should be:
keypath.addObserver(self, //self is observing keypath..
forKeyPath: "backgroundColor",
options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.old],
context: nil)
The first parameter of addObserver needs to be the class that wants to do the observing.
That change will cause it to print: background changed.. Don't forget to remove the observer later.

Using KVC with Singleton pattern

My questionwas about if it was possible to use KVC on a Singleton property on Swift. I was testing KVC on a class was able to get it working but decided to see if it work on a Singleton class.
I'm running into an error stating that the "shared" property of my Singleton isn't KVC-compliant.
class KVOObject: NSObject {
#objc static let shared = KVOObject()
private override init(){}
#objc dynamic var fontSize = 18
}
override func viewDidLoad() {
super.viewDidLoad()
addObserver(self, forKeyPath: #keyPath(KVOObject.shared.fontSize), options: [.old, .new], context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == #keyPath(KVOObject.shared.fontSize) {
// do something
}
}
I am currently getting the error below:
NetworkCollectionTest[9714:452848] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ addObserver: forKeyPath:#"shared.fontSize" options:3 context:0x0] was sent to an object that is not KVC-compliant for the "shared" property.'
The key path is not correct. It’s KVOObject.fontSize. And you need to add the observer to that singleton:
KVOObject.shared.addObserver(self, forKeyPath: #keyPath(KVOObject.fontSize), options: [.old, .new], context: nil)
As an aside, (a) you should probably use a context to identify whether you're handling this or whether it might be used by the superclass; (b) you should call the super implementation if it's not yours; and (c) make sure to remove the observer on deinit:
class ViewController: UICollectionViewController {
private var observerContext = 0
override func viewDidLoad() {
super.viewDidLoad()
KVOObject.shared.addObserver(self, forKeyPath: #keyPath(KVOObject.fontSize), options: [.new, .old], context: &observerContext)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if context == &observerContext {
// do something
} else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
deinit {
KVOObject.shared.removeObserver(self, forKeyPath: #keyPath(KVOObject.fontSize))
}
...
}
Or, if in Swift 4, it's now much easier as it's closure-based (avoiding need for context) and is automatically removed when the NSKeyValueObservation falls out of scope:
class ViewController: UICollectionViewController {
private var token: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
token = KVOObject.shared.observe(\.fontSize, options: [.new, .old]) { [weak self] object, change in
// do something
}
}
...
}
By the way, a few observations on the singleton:
The shared property does not require #objc qualifier; only the property being observed needs that; and
The init method really should be calling super; and
I'd probably also declare it to be final to avoid confusion that can result in subclassing singletons.
Thus:
final class KVOObject: NSObject {
static let shared = KVOObject()
override private init() { super.init() }
#objc dynamic var fontSize: Int = 18
}

Resizable UIWebView/WKWebView contentView in UITablewViewCell

I have UIWebView in UITableViewCell (url for ex.: http://advisa.work/bank_partner/webview01.html , it has resizable content height).
So I observe it contentSize
func startObservingHeight() {
let options = NSKeyValueObservingOptions([.new])
webView.addObserver(self, forKeyPath: "scrollView.contentSize", options: options, context: nil)
}
So code below is called only when contentSize of webView is growing. So I increase my tableViewCell, and when I shrink some parts of html document (css changes), function below not calling, and I cannot resize cell.
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let zeChange = change as? [NSKeyValueChangeKey: NSValue] {
let newSize = zeChange[NSKeyValueChangeKey.newKey]?.cgSizeValue
changeHeight?(newSize.height)
}
}
this my callback variable:
var changeHeight: ((CGFloat?) -> ())?

How to show user location on Google Maps iOS SDK

I want to present the blue dot on the map that represents the user's location.
I've tried to do it like that:
override func viewWillAppear(animated: Bool) {
self.mapView.addObserver(self, forKeyPath: "myLocation", options: NSKeyValueObservingOptions.New, context: nil)
}
deinit {
self.mapView.removeObserver(self, forKeyPath: "myLocation")
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if keyPath == "myLocation" {
self.mapView.animateToLocation((object?.myLocation!!.coordinate)!)
self.mapView.animateToZoom(17)
}
}
but observeValueForKeyPath(keyPath: ofObject: change: context:) doesn't called and I can't understand why.
Thank you very much!
You can do this with following line of code. This will bring up the google's default blue dot on users current location.
self.mapView.myLocationEnabled = true

Resources