10

I need to duplicate a view, with all it's subviews.

Does anyone knows how to do it?

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
mxg
  • 20,946
  • 12
  • 59
  • 80

6 Answers6

24

The easiest (but probably not the fastest) way is to archive it into an NSData object and unarchive it right away.

NSData *tempArchive = [NSKeyedArchiver archivedDataWithRootObject:myView];
UIView *myViewDuplicate = [NSKeyedUnarchiver unarchiveObjectWithData:tempArchive];
Ole Begemann
  • 135,006
  • 31
  • 278
  • 256
  • 11
    Unfortunately, this won't work if your view has any subviews/objects that can't be archived (such as a UIImage). – rcw3 Jan 20 '11 at 23:55
  • So you're saying that this won't work for UIImageViews? – fatuhoku May 13 '14 at 22:24
  • @IulianOnofrei did you find any solution to use it for UIImageView in instance of UIView? – Akshay Sunderwani Aug 07 '15 at 11:28
  • @AkshaySunderwani, No :( I did find [this library](UIView-Copy) but I didn't test it yet. Let me know if you test it whether it works or not. – Iulian Onofrei Aug 07 '15 at 16:49
  • @lulianOnofrei i got a alternate for uiimage view, by creating a CGImageRef of original UIView if it of kind UIImageView, then use the following approach and copy CGImageRef to duplicate or your copy of UIView. You can have more info on this from this answer http://stackoverflow.com/a/26214969/3824808 – Akshay Sunderwani Aug 08 '15 at 02:02
  • According to [this answer](http://stackoverflow.com/a/9334593/3681880), it should also work with `UIImageView`. – Suragch May 05 '16 at 12:44
  • It does not work for UIImageView on iOS8. Works fine on 10 and 11. – AlexeyVMP Sep 26 '17 at 22:57
3

To archive any uiimage object simply convert them to an imagerep of one kind or another (PNG for instance) and archive that NSData object, then in the unarchive you restore the uiimage with imageWithData:

April
  • 31
  • 1
0

Here is a new method you can use: Use UIView's method:

- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates

This is the fastest way to draw a view. Available in iOS 7.

Lei Zhang
  • 500
  • 4
  • 15
0

One can easily copy a UIView by archiving and unarchiving process.

NSData *tempArchive = [NSKeyedArchiver archivedDataWithRootObject:myView];
UIView *myViewDuplicate = [NSKeyedUnarchiver unarchiveObjectWithData:tempArchive];

But this ignores UIImageView, so for UIImageView do it by this way,

UIImage *img = [UIImage imageNamed: @"myImage"];
UIImage *img2 = [UIImage imageWithCGImage:img.CGImage scale:img.scale orientation:img.imageOrientation];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:img2];
UIImage *imgCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"%@, %@", imgCopy, NSStringFromCGSize(imgCopy.size)); // -> <UIImage: 0x7fa013e766f0>, {50, 53}

Refrence from here.

Community
  • 1
  • 1
Akshay Sunderwani
  • 12,428
  • 8
  • 29
  • 52
0

Swift version

This answer shows how to do the same thing in Swift as the accepted answer does in Objective-C.

let tempArchive = NSKeyedArchiver.archivedDataWithRootObject(myView)
let myViewDuplicate = NSKeyedUnarchiver.unarchiveObjectWithData(tempArchive) as! UIView

My full answer with more notes is here.

Community
  • 1
  • 1
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
0

enter image description here

You can drag your custom view outside the root view of the viewController. In the implementation of the custom view class, override the enconde/decode methods. Realize the IBOutlets and IBActions connections will not be archived in memory.

@implementation MyCustomView

-(MyCustomView*)aCopy{
    NSData *temp = [NSKeyedArchiver archivedDataWithRootObject:self];
    return [NSKeyedUnarchiver unarchiveObjectWithData:temp];
}


- (void)encodeWithCoder:(NSCoder *)coder {
    [super encodeWithCoder:coder];

    [coder encodeObject:self.btnBack forKey:@"btnBack"];
    [coder encodeObject:self.lbl1 forKey:@"lbl1"];
}

-(instancetype)initWithCoder:(NSCoder *)aDecoder{

    self = [super initWithCoder:aDecoder];
    if (self) {
        self.btnBack = [aDecoder decodeObjectForKey:@"btnBack"];
        self.lbl1 = [aDecoder decodeObjectForKey:@"lbl1"];
        [_btnBack addTarget:self action:@selector(onBtnBack) forControlEvents:UIControlEventTouchUpInside];
    }
    return self;
}

- (IBAction)onBtnBack{
    //. . .

MyCustomView.h:

#import <UIKit/UIKit.h>

@interface MyCustomView : UIView

@property (weak, nonatomic) IBOutlet UILabel *lbl1;
@property (weak, nonatomic) IBOutlet UIButton *btnBack;

-(MyCustomView*)aCopy;

@end

Sample of reusing the custom view in ViewController.m:

#import "ViewController.h"
#import "MyCustomView.h"

@interface ViewController ()
@property (strong, nonatomic) IBOutlet MyCustomView *myCustomView;
@property (weak, nonatomic) IBOutlet UIView *vwPlaceHolder;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:_myCustomView];
}

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    MyCustomView *vwCopy = [_myCustomView aCopy];
    vwCopy.lbl1.text = @"My other msg";
    vwCopy.frame = _vwPlaceHolder.frame;
    [_vwPlaceHolder removeFromSuperview];
    [self.view addSubview:vwCopy];

}
@end
Miguel Gallego
  • 427
  • 4
  • 7