14

I can't see any way to copy an NSView and create an identical NSView object. I see google hits about "use an NSData" but I don't understand that.

Nektarios
  • 10,173
  • 8
  • 63
  • 93

2 Answers2

26

To straight up "copy" an NSView, the view has to implement the NSCopying protocol. Unfortunately, NSView does not.

Fortunately, it does implement the NSCoding protocol, which means we can still duplicate a view like:

NSData * archivedView = [NSKeyedArchiver archivedDataWithRootObject:myView];
NSView * myViewCopy = [NSKeyedUnarchiver unarchiveObjectWithData:archivedView];

And voilá! You now have a duplicate of myView.


Edit: (Swift version)

let archivedView = NSKeyedArchiver.archivedData(withRootObject: myView)
let myViewCopy = NSKeyedUnarchiver.unarchiveObject(with: archivedView)

(archivedView is of type Data, not NSData)

bshirley
  • 8,217
  • 1
  • 37
  • 43
Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
  • 4
    Note that if you subclass, you'll need to implement archiving support. The docs know all. – bbum Jun 24 '10 at 07:25
  • In every case that I subclass - even when I don't have data added but only a couple methods? – Nektarios Jun 24 '10 at 16:09
  • Nektarios: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmImplementCopy.html – Peter Hosey Jun 25 '10 at 07:59
  • Lifesaver. I waded through a few results in Google and confusion until I found this answer. Thanks! – John Gallagher Jun 02 '11 at 14:10
  • Unarchiving doesn't work for me when using an NSTextField. It throws the error `"This decoder will only decode classes that adopt NSSecureCoding. Class 'NSTextField' does not adopt it."`. I can't for the life of me figure our a way around this. – Noah Nuebling Jul 25 '21 at 19:13
  • I found a solution! See my answer to this question. – Noah Nuebling Jul 25 '21 at 21:09
3

Since around iOS 12 / macOS 14, the accepted answer by @Dave DeLong doesn't work anymore in all cases.

The problem is, that some of the methods which were used in @Dave DeLongs solution don't work anymore for objects that only adhere to the NSCoding protocol but not the NSSecureCoding protocol.


Solution

Luckily, there's still a solution that works for copying any Object that adheres to NSCoding (and not only ones that adhere to NSSecureCoding) even on the newest operating systems!

There are a few changes in this solution compared to the old one. Most importantly, you'll have to create an instance of NSKeyedUnarchiver instead of using the more convenient class methods. The convenient class methods only support NSSecureCoding but not NSCoding in the newer operating systems.

My implementation looks like this:

public func insecureCopy<T: NSCoding>(of original: T) throws -> T {
    
    /// See https://developer.apple.com/forums/thread/107533
    
    if #available(macOS 10.13, *) {
    
        let data = try NSKeyedArchiver.archivedData(withRootObject: original, requiringSecureCoding: false)
        
        let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data)
        unarchiver.requiresSecureCoding = false
        
        let copy = unarchiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as! T
        
        return copy
        
    } else { /// Fallback (untested)
        
        let data = NSKeyedArchiver.archivedData(withRootObject: original)
        
        let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
        unarchiver.requiresSecureCoding = false
        
        let copy = unarchiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey) as! T
        
        return copy
        
    }
}

Notes

  • The source for this solution was from @eskimos great answer on this Apple Dev Forum Post: https://developer.apple.com/forums/thread/107533
    as well as the WWDC session video that they linked to.
  • The method is called insecureCopy() because it works on non-secure coding objects. But, as far as I understand, this method is not in fact insecure. Working with non-secure NSCoding objects is only insecure if you save and load insecure archives to and from a file as far as I understand. Not if you just use the archives for copying like in this case.
  • The fallback code for macOS 10.13 and earlier is not tested.
Noah Nuebling
  • 219
  • 2
  • 11