How to copy a UIView object by value / clone

Discussion in 'iOS Programming' started by daproject85, Sep 8, 2012.

  1. macrumors member

    Joined:
    Apr 13, 2011
    #1
    Hi Forum,

    I have a bit of an issue here. So i have a UIViewController and it has a UIView property in which i draw stuff in and such. Now i have a UITableViewController with a UITableView and in my i want to have a custom cell in which i want to have "thumbnail" of the UIView. Each row is a Y= graph function.. etc y=2x. I have a delegate method which UITableViewController subclass calls and my UIGraphViewController implements in which i simply did...

    Code:
    -(UIView *)calculatorProgramsTableViewController:(CalculatorProgramTableViewController *)sender wantsFavProgramThumbnail:(id)program
    {
        self.program = program; //makes graphview (UIview object) draw the function first 
        return self.graphview; // returns the UIview object
    }
    
    but the problem is that once i do get the object in my UITableViewController sublclass.....well its passed by reference clearly so if i modify it the original view gets modified too. and it goes away when you go back to it. Please help
     
  2. macrumors 6502

    Joined:
    Nov 5, 2010
    Location:
    Sea of Tranquility
    #2
    Hmm, you can always do

    Code:
    UIView *myColneView = [myOriginalView copy];
    
    Any changes you make to the clone view will not be performed on the original view, i.e. they will be two different objects.

    Also, if the view is static (no animations/updating contents), you can take a screenshot of your view and display it in your table.
     
  3. thread starter macrumors member

    Joined:
    Apr 13, 2011
    #3
    hey thanks for the reply my friend. So would i have to declare NSCOPYING poroto?

    and would i have to implement the copywithzone or copy method? or its ready to use out of the box?
     
  4. macrumors 6502

    Joined:
    Nov 5, 2010
    Location:
    Sea of Tranquility
    #4

    I am sorry I posted in haste. UIView does not confirm to the NSCopying protocol, and enabling it can be a bit of a hassle.

    If I were you, I would save all the parameters that define your view as properties/ivars of the view itself. And then I would just create a new view with the exact same properties as the original.
     
  5. thread starter macrumors member

    Joined:
    Apr 13, 2011
    #5
    Wait so I am a bit confused :) so doing

    Uiview *cloneview = [originalView copy]

    Will not work? U don't need nscopying for that?
     
  6. KnightWRX, Sep 9, 2012
    Last edited: Sep 9, 2012

    macrumors Pentium

    KnightWRX

    Joined:
    Jan 28, 2009
    Location:
    Quebec, Canada
    #6
    How do you draw the UIView thumbnail exactly ? Instead of just using the reference to your UIView, you could force it to render itself once, by using its layer's methods, in the current context for your UITableView.

    Something like :

    Code:
    [myView.layer renderInContext: UIGraphicsGetCurrentContext];
    
    Without your thumbnail generation code, kinda hard to see how you could implement this though.

    EDIT: Why not use UIImageView for the resulting thumbnail ? Even easier, just return the UIImageView :

    Code:
    -(UIImageView *)calculatorProgramsTableViewController:(CalculatorProgramTableViewController *)sender wantsFavProgramThumbnail:(id)program
    {
        self.program = program; //makes graphview (UIview object) draw the function first 
        UIGraphicsBeginImageContext([self.graphview.layer frame].size);
    
        [self.graphview.layer renderInContext:UIGraphicsGetCurrentContext()];
        UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
    
        UIGraphicsEndImageContext();
    
        return [[[UIImageView alloc] initWithImage: outputImage] autorelease];
    }
     
  7. macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #7
    In general, you don't want to copy a view to save it's state.

    iOS programming heavily relies on the MVC (Model View Controller) design pattern. In that design pattern, model objects store data, view objects display it, and controller objects mediate between the two and provide control logic.

    A UIView is a view object. It should not be used to store data, only display it. What I would suggest doing is creating a data container object that describes all the settings (state values) that one of your custom view objects needs to display itself. Then give your view object a data container object as a property. Then your view controller can set up and install a data object in one of your views. That would trigger the view to draw itself.

    You can implement NSCopying in your data container object, which is much cleaner and simpler. You'd just copy all the properties of your data container to the new copy.

    The other posters have made good suggestions about how to handle the thumbnail issue. To create a thumbnail, create a graphics context and ask your view's layer to render itself into that context. Then copy the contents of the context into a UIImage and dispose of the context. (I think somebody already posted the code for those steps.) You can then use that image as a static snapshot of your custom view.
     
  8. thread starter macrumors member

    Joined:
    Apr 13, 2011
    #8
    Duncan,

    thanks so much for the good info. I went with the second route that you suggested. I made a UIImage of my view and just made it small to fit in the cell. Here is my Code

    Code:
     
        UIGraphicsBeginImageContext(self.graphview.bounds.size);
        [self.graphview.layer renderInContext:UIGraphicsGetCurrentContext()];
        UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    
    I am not going to lie... i do not really understand line 1, and 2.... what i am really trying to say is... yea sure i read the API but i dont really understand what they do or how would one know they would have to code line 1, then call the "renderIncontext on the layer ...and also whats the significance of the "layer" property
     
  9. macrumors Pentium

    KnightWRX

    Joined:
    Jan 28, 2009
    Location:
    Quebec, Canada
    #9
    The context is simple. You're creating a context you can have a handle on and control (get and set properties to, render in, etc..). A context is simply a big area of memory where any Quartz command will be applied, a sort of frame buffer, but being that you have control over it, it's simply your own output buffer for drawing commands.

    For the layer property, you need to understand how the Quartz Extreme model works. Basically, everything is a layer that is submitted to compositor before being sent to the system frame buffer for display. Your application has layers, the OS has layers, other applications have layers. Depending on the position (z-index) of these layers, the transparency of the pixels on the layers, the compositor (Quartz) will blend or choose pixels accordingly to copy to the final frame buffer for display.

    When you ask a View to render its layer in a context, you're asking it to basically give you what it is sending to the compositor, but in a memory space you own rather than the system's compositor. Then you can do what you wish with the layer info, modify it, apply it to another view's layer or... as you did here, generate a thumbnail of what that view (and only that view) contains.

    The UI* methods from UIKit are simply an abstraction over the existing Quartz functions (CG prefixed functions, like CGCurrentContext() or CGBitmapContextCreate, etc..). If you really want to understand how this works, I would suggest reading the Quartz Programming guide available from Apple.
     
  10. thread starter macrumors member

    Joined:
    Apr 13, 2011
    #10
    thanks alot KnightWRX

    so i am going to try to take a stab at this...
    Line 1: basically we have to say here is the size of the area I want to work on or what I am modifying would be this size.

    Line 2: we take the GraphView layer (the sheet we drew graphview on with all its subviews etc) and we say this is the layer we want to do all the rendering in.

    Did i get it right?
    and 1 question: renderInContext:UIGraphicsGetCurrentContext() , the how does the UIgraphicsGetCurrentContext know what is the "current context"? is it from line 1 where I said the size? or what
     

Share This Page