在Swift中创建UIView的副本

时间:2014-11-25 08:16:13

标签: ios swift uiview

因为对象是引用类型而不是值类型,所以如果将UIView设置为等于另一个UIView,则视图是同一个对象。如果你修改了一个,你也可以修改另一个。

我有一个有趣的情况,我想在另一个视图中添加UIView作为子视图,然后我做了一些修改,这些修改不应该影响原始的UIView。如何制作UIView的副本,以便我可以确保将该副本添加为子视图而不是对原始UIView的引用?

请注意,我无法像创建原始文件一样重新创建视图,我需要一些方法来创建给定任何UIView对象的副本。

8 个答案:

答案 0 :(得分:105)

您可以制作UIView扩展程序。在下面的示例代码段中,函数copyView返回AnyObject,因此您可以复制UIView的任何子类, UIImageView。如果您只想复制 UIView,可以将返回类型更改为UIView。

//MARK: - UIView Extensions

extension UIView
{
    func copyView<T: UIView>() -> T {
        return NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: self)) as! T
    }
}

使用示例:

let sourceView = UIView()
let copiedView: UIView = sourceView.copyView()

答案 1 :(得分:16)

您无法任意复制对象。只能复制实现NSCopying协议的对象。

但是,有一种解决方法:由于UIView可以序列化为磁盘(例如从XIB加载),因此可以使用NSKeyedArchiverNSKeyedUnarchiver来创建序列化{ {1}}描述您的视图,然后再次反序列化以获得一个独立但相同的对象。

答案 2 :(得分:5)

iOS 12.0更新

从iOS 12.0开始不推荐使用方法archivedData(withRootObject:)unarchivedObject(with:)

这是@Ivan Porcolab使用较新API(自11.0起)的答案的更新,它也变得更加通用以支持其他类型。

extension NSObject {
    func copyObject<T:NSObject>() throws -> T? {
        let data = try NSKeyedArchiver.archivedData(withRootObject:self, requiringSecureCoding:false)
        return try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? T
    }
}

答案 3 :(得分:4)

我认为您应该将UIView与.nib链接起来,然后创建一个新的。

属性不一样,但你保持外观和方法。

答案 4 :(得分:4)

这个答案显示了如何做@uliwitness建议的内容。也就是说,通过归档然后取消归档来获取相同的对象。 (这也基本上是Ivan Porkolab在他的回答中所做的,但我认为它的格式更易读。)

let myView = UIView()

// create an NSData object from myView
let archive = NSKeyedArchiver.archivedData(withRootObject: myView)

// create a clone by unarchiving the NSData
let myViewCopy = NSKeyedUnarchiver.unarchiveObject(with: archive) as! UIView

注释

  • 取消归档数据会创建AnyObject类型的对象。我们使用as! UIView键入将其强制转换回UIView,因为我们知道它是什么。如果我们的观点是UITextView,那么我们可以输入它as! UITextView
  • myViewCopy不再有父视图。
  • Some people在使用UIImage时提到了一些问题。不过,请参阅thisthis回答。

已更新至Swift 3.0

答案 5 :(得分:1)

您必须使用模式原型

原型示例:

class ThieveryCorporationPersonDisplay {
    var name: String?
    let font: String

    init(font: String) {
        self.font = font
    }

    func clone() -> ThieveryCorporationPersonDisplay {
        return ThieveryCorporationPersonDisplay(font:self.font)
    }
}

使用原型的一个例子:

let Prototype = ThieveryCorporationPersonDisplay(font:"Ubuntu")

let Philippe = Prototype.clone()
Philippe.name = "Philippe"

let Christoph = Prototype.clone()
Christoph.name = "Christoph"

let Eduardo = Prototype.clone()
Eduardo.name = "Eduardo"

答案 6 :(得分:0)

添加解决方案可能只是创建一个新的UIView,然后复制任何关键属性。这可能不适用于OP的情况,但它可以很好地适用于其他情况。

例如,使用UITextView,您可能只需要框架和属性文字:

let textViewCopy = UITextView(frame: textView.frame)
textViewCopy.attributedText = textView.attributedText 

答案 7 :(得分:-1)

此外,您可以使用此模式复制View controller视图

let vc = UIViewController() 
let anotherVc = UIViewController()
vc.view = anotherVc.copyView()

您可能需要使用它来缓存视图控制器或克隆。