Skip to main content

iPhone: Create a reusable component (control) that has some Interface Builder pieces and some code



I want to create a reusable component (a custom control) for the iPhone. It consists of several standard controls prearranged on a View, and then some associated code. My goals are:





  1. I want to be able to use Interface Builder to lay out the subviews in my custom control;



  2. I want to somehow package the whole thing up so that I can then fairly easily drag and drop the resulting custom component into other Views, without having to manually rewire a bunch of outlets and so on. (A little manual rewiring is fine, I just don't want to do tons and tons of it.)







Let me be more concrete, and tell you specifically what my control is supposed to do. In my app, I sometimes need to hit a web service to validate data that the user has entered. While waiting for a reply from the web service, I want to display a spinner (an activity indicator). If the web services replies with a success code, I want to display a "success" checkmark. If the web service replies with an error code, I want to display an error icon and an error message.





The single-use way to do this is pretty easy: I just create a UIView that contains a UIActivityIndicatorView, two UIImages (one for the success icon and one for the error icon), and a UILabel for the error message. Here's a screenshot, with the relevant parts marked in red:





alt text





I then wire up the pieces to outlets, and I put some code in my controller.





But how do I package up those pieces -- the code and the little collection of views -- so that I can reuse them? Here are a few things I found that get me partway there, but aren't that great:





  • I can drag the collection of views and controls into the Custom Objects section of the Library; then, later, I can drag them back out onto other views. But (a) it forgets which images were associated with the two UIImages, (b) there is a lot of manual rewiring of four or five outlets, and (c) most importantly, this doesn't do bring along the code. (Perhaps there's an easy way to wire up the code?)



  • I think I could create an IBPlugin; not sure if that would help, and it seems like a lot of work, and also it's not entirely clear to me whether IBPlugins work for iPhone development.



  • I thought, "Hmm, there's code associated with this -- that smells like a controller," so I tried creating a custom controller (e.g. WebServiceValidatorController ) with associated XIB file. That actually feels really promising, but then at that point I can't figure out how, in Interface Builder, to drag this component onto other views. The WebServiceValidatorController is a controller, not a view, so I can drag it into a Document Window, but not into a view.







I have a feeling I'm missing something obvious...


Comments

  1. I've created similar constructs except that I do not use the result in IB, but instantiate using the code. I'll describe how that works, and at the end I'll give you a hint how that can be used to accomplish what you're after.

    I start from an empty XIB file where I add one custom view at the top level. I configure that custom view to be my class. Below in view hierarchy I create and configure subviews as required.

    I create all IBOutlets in my custom-view class, and connect them there. In this exercise I ignore the "File's owner" completely.

    Now, when I need to create the view (usually in controller as part of while/for-loop to create as much of them as needed), I use NSBundle's functionality like this:

    - (void)viewDidLoad
    {
    CGRect fooBarViewFrame = CGRectMake(0, 0, self.view.bounds.size.width, FOOBARVIEW_HEIGHT);
    for (MBSomeData *data in self.dataArray) {
    FooBarView *fooBarView = [self loadFooBarViewForData:data];
    fooBarView.frame = fooBarViewFrame;
    [self.view addSubview:fooBarView];

    fooBarViewFrame = CGRectOffset(fooBarViewFrame, 0, FOOBARVIEW_HEIGHT);
    }
    }

    - (FooBarView*)loadFooBarViewForData:(MBSomeData*)data
    {
    NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"FooBarView" owner:self options:nil];
    FooBarView *fooBarView = [topLevelObjects objectAtIndex:0];
    fooBarView.barView.amountInEuro = data.amountInEuro;
    fooBarView.barView.gradientStartColor = data.color1;
    fooBarView.barView.gradientMidColor = data.color2;
    fooBarView.titleLabel.text = data.name;
    fooBarView.titleLabel.textColor = data.nameColor;
    return fooBarView;
    }


    Notice, how I set owner of nib to self - there's no harm as I didn't connect "File's owner" to anything. The only valuable result from loading this nib is its first element - my view.

    If you want to adapt this approach for creating views in IB, it's pretty easy. Implement loading of subview in a main custom view. Set the subview's frame to main view's bounds, so that they are the same size. The main view will become the container for your real custom view, and also an interface for external code - you open only needed properties of its subviews, the rest is encapsulated. Then you just drop custom view in IB, configure it to be your class, and use it as usual.

    ReplyDelete

Post a Comment

Popular posts from this blog

[韓日関係] 首相含む大幅な内閣改造の可能性…早ければ来月10日ごろ=韓国

div not scrolling properly with slimScroll plugin

I am using the slimScroll plugin for jQuery by Piotr Rochala Which is a great plugin for nice scrollbars on most browsers but I am stuck because I am using it for a chat box and whenever the user appends new text to the boxit does scroll using the .scrollTop() method however the plugin's scrollbar doesnt scroll with it and when the user wants to look though the chat history it will start scrolling from near the top. I have made a quick demo of my situation http://jsfiddle.net/DY9CT/2/ Does anyone know how to solve this problem?

Why does this javascript based printing cause Safari to refresh the page?

The page I am working on has a javascript function executed to print parts of the page. For some reason, printing in Safari, causes the window to somehow update. I say somehow, because it does not really refresh as in reload the page, but rather it starts the "rendering" of the page from start, i.e. scroll to top, flash animations start from 0, and so forth. The effect is reproduced by this fiddle: http://jsfiddle.net/fYmnB/ Clicking the print button and finishing or cancelling a print in Safari causes the screen to "go white" for a sec, which in my real website manifests itself as something "like" a reload. While running print button with, let's say, Firefox, just opens and closes the print dialogue without affecting the fiddle page in any way. Is there something with my way of calling the browsers print method that causes this, or how can it be explained - and preferably, avoided? P.S.: On my real site the same occurs with Chrome. In the ex