XCode create a uiview on top of a uitableview - iphone

I am having some problems and wondering if someone could help.
I have always used storyboards so I am rusty at programmatically creating UIViews.
Basically, I am loading data from an API and displaying it in a UITableView. The problem is, on one part of my app, the loading of the data takes at least 10 seconds as it loads 10 different things from the API. (This must use separate calls). I use a sleep of 1 second so as not to make too many calls to the API per second (Hence why it takes so long to load). This is integral to my app.
I have implemented a count which increments after each call. Now all I am hoping to do, is create a UIView with a label and progress bar to say that it is loading and how much. This must be displayed over the tableview.
How do I go about doing this?

I do it this way:
1 - Create a UIView subclass, with outlets to all of the view's sub elements.
2 - Create a xib file. Change the class of the top view to the name of your UIView subclass. Add all the elements you want to the view and then hook up the outlets from the topmost view to the elements. File's Owner remains blank and unused.
3 - In the view controller that I want to use the view in I add:
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:#"MyCustomView"
owner:self
options:nil];
MyCustomView *myCustomView = [nibContents objectAtIndex:0];
Then you can add the custom view to your table view. I would probably add a property in the view controller and assign the custom view to it so I could access the custom view using self.myCustomView.

Related

reuse view from storyboard

I have a tableview with custom section headers. The view for the section header is defined in the storyboard and wired to an instance variable. Is there a way to request a new instance of the view from the storyboard?
In the past I have done this by having the section header defined in its own xib file and getting a new instance by using
[[NSBundle mainBundle] loadNibNamed:#"TimerViewSectionHeader" owner:self options:nil];
UIView *newHeaderView = self.sectionHeaderView;
I dont' think there is a way to do that. Best bet is to put the tableview custom header view in a separate nib and load it like you did in your code sample whenever you need to use it.
I tried to do the same thing and ran into the same problem.
I like to work with storyboards a lot and was impressed how fast I could create a working UI. However, as soon as you need to re-use views it makes a lot of sense to put those into a separate nib along with its UIViewController subclass.
You can then place a generic UIView in all the places where your re-used view should go and add the view using your ViewController:
[myReusableViewController loadView];
[myReusableViewController viewDidLoad]; // You have to handle view callbacks yourself.
[self.myReusableViewPlaceholder addSubview:myResusableViewController.view];
[myReusableViewController viewWillAppear:YES];
So to sum it up:
Use storyboard, it's great
Create the scaffold of your application in the storyboard, along with any static view (like About screens etc.)
Create re-used views in a custom nib + UIViewController subclass and add UIView placeholders in your storyboard.
In another answer I thought about some Pros and Cons of Storyboard
The solution I've come up with for this is as follows:
I have a tableview with multiple prototype cells that displays complex data. There is a segue to a detail view, and a transaction process view.
This first tableview has a search button that displays a new tableview with the results. It needs the same functionality as the main tableview that pushes it; including segues to the detail and transaction progress views so:
On storyboard, select and copy your main tableview. Deselect and paste. Create a push segue from your main tableview to your 2nd tableview; or from where ever you want to navigate to it from. Modify the 2nd tableview as you like. IE: If it requires some UI changes no problem.
Create a new viewcontroller class that is a subclass of the viewcontroller running the main tableview.
Override the data delegate in your subclass to serve up the subset of data you want.
Back in the storyboard, select your 2nd tableview controller and in the identity inspector select your subclass as the custom class.
For this solution to work smoothly, your app really needs to be managing data for the views. You could use prepareforsegue to pass data from 1st tableview to the second, but I've found the app data model far more flexible from numerous points of view.
Unless you have buttons that push to the sub views via segue, your subclass will need to override functions that push via segues with identities. NB Segues must have unique identifiers if you id them at all.
It took a lot of trial and error to figure this out, but once you understand the concept, it's a relatively smooth solution that is quite adaptable and not so bad to implement.
I am not sure about just views, but the way that I was able to get view controllers out of my storyboard is as follows.
UIViewController *viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"IdentifierName"];
From here, perhaps you might be able to use this similarly to how it was once done with nibs.
I've been able to reuse a view in the storyboard just by connecting a transition from one tableview into the one I want to reuse.
so my tableview that I want to reuse is pointed to twice.
It sort of works but the problem I'm running into it setting a variable (using instantiateViewControllerWithIdentifier) in my app delegate to my table view that is getting reused.
It seems that if I reuse it, the storyboard is creating 2 instances of my tableview and the one I get with instantiateViewControllerWithIdentifier isn't the one I want.
I'm not really sure if this is the proper way to do it. But I assume many others are doing this somehow. With the custom table cells in storyboard I suspect lots of people want to reuse their views.
For example: We want to reuse the view(include subviews) in storyboard shown below.
The best solution I know so far is clip and paste the view related code to the New Singe View file without losing the information.
Detailed steps are as follows
Step 1: Rename the view we want reuse. Just prepare for step 2.
Step 2: Open storyboard as source code in order to clip the XML code we need
Step 3、4: Search and clip the code we need
Step 4.5(Not needed): Open as Interface Builder to see the view removed
Step 5、6: New XXX.xib and paste the code we clipped just now
Step 7: Important. Insert code<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> to XXX.xib source code.
Warning: Do this before open it as Interface Builder! Otherwise, you will see wrong size and layout waring.
[![step 7][9]][9]
Step 8: New XXX.swift to connect the XXX.xib
[![step 8][10]][10]
Step 9: Add the view anywhere we want
[![step 9][11]][11]
I get warning: "You need at least 10 reputation to post more than 8 links."
Can you support me to upload the remaining 3 screenshots?

What is the best way to load a custom UIView?

In my iPhone app, I made a custom UIView that I use in several different view controllers to display some information.
I'm familiar with making customer table cells for use in a table but I've never made a custom view again.
My question is what is the best way to load this view? I've tried simply placing the view in the view controllers I want it to appear in using IB, but that doesn't seem to be enough. It seems to me that it would make sense that if you set the Class Identity for the view that it should load up that view when the view controller is created. Or maybe it doesn't because it doesn't know what nib to get it from (it's in its own nib, of course).
Since it's not a view controller, I can't just use initWithNibName. I've tried using NSBundle loadNibNamed like I do with table cells but it seems like an awfully large amount of work. The code looks like this:
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"MyCustomView" owner:nil options:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[MyCustomView class]]) {
self.customView = currentObject;
break;
}
}
[self.view addSubview:self.customView];
There must be an easier way which doesn't involve the loop. Also, creating the view this way makes me lose all the info from IB on where I actually placed the view. It's created at 0,0 which is not where I want it. Yes, I know I can set a new frame, but again, there must be an easier way. Is there a way I can do this all through IB? Or at least with less work programmatically?
I also make it that way. I ran into the problem that I want to subclass UIView and using nib at the same time. I don't think there's better way to do so, because nib file can contains many objects and you have to specify which one to use. However, do it this way you can put IBOutlet in your MyCustomView and bind it in IB so the view can manage itself e.g. overriding layoutSubviews.
Try this: create a new UIViewController subclass (called ParentViewController for the sake of answer) that each of your controllers that need to display this information will then also subclass. This controller should not have a nib associated with it.
In ParentViewController, create an IBOutlet for a UIView that will hold a reference to a view containing your custom nib. (Called customView, again just because)
Now, open your nib (for the commonly used customView) in Interface Builder and select the File's Owner. Now in the Identity tab, choose the ParentViewController created above for the class. Now here comes the trick. In Connections, remove any connection to "view" (if there is any) and create a connection from the customView outlet to the topmost UIView of the nib that defines your customView.
Now, when you do a loadNibNamed in the code for a subclass of ParentViewController, it should automagically load the UIView from your nib and put a reference to it in the outlet for you to use. No loop required.
You could also add outlets for any other interface components in your nib and do the same thing.
Good luck!

How to load a UIView from a NIB?

I have been using UIViewControllers and initWithNibName with much success, basically using them as a convenient way to design the view with Interface Builder. Unfortunately I have built a hierarchy of views before noticing this line in the UIViewController documentation:
Note: You should not use view
controllers to manage views that fill
only a part of their window
My question is this: Having a very simple NIB that only has a UIView in addition to the default First Responder and Owning Object, what is the simplest way to load the UIView into my code?
I have not been able to get loadNibNamed:owner:options: to work at this point, but suspect the answer will involve it somehow.
Yes, just call
[[NSBundle mainBundle] loadNibNamed:#"viewNib" owner:self options:nil];
You normally do this from the view controller you have set as File's Owner in the NIB. That way, you can declare an outlet for the view in the view controller which will automatically get connected when you load the NIB file. You don't even have to work with the return value of the method in this case.

Can a View Controller manage more than 1 nib based view?

I have a VC controlling a screen of content that has 2 modes; a normal mode and an edit mode.
Can I create a single VC with 2 views, each from separate nibs?
In many situations on the iphone, you have a VC which controls an associated view. Then on
a button press or other event, a new VC is loaded and its view becomes the top level view etc.
But in this situation, I have 2 modes that I want to use the same
VC for, because they are closely related. So I want a VC which can swap in/out 2 views.
As per here:
How to load a UIView using a nib file created with Interface Builder
I have found that I can load a VC with an associated view from a nib and then later on load
a different view from another nib and make that new view the active view.
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"EditMode" owner:self options:nil];
UIView *theEditView = [nibObjects objectAtIndex:0];
self.editView = theEditView;
[self.view addSubview:theEditView];
The secondary nib has outlets wired up to the VC like the primary nib. When the new nib is loaded,
the outlets are all connected up fine and everything works nicely. Unfortunately when this edit
view is then removed, there doesn't seem to be any elegant way of getting the outlets hooked up
again to the (normal mode) view from the original nib. Nib loading and outlet setting seems a
once only thing.
So, if you want to have a VC that swaps in/out 2 views without creating a new VC, what are the options?
1) You can do everything in code, but I want to use nibs because it makes creating the UI simpler.
2) You have 1 nib for your VC and just hide/show elements using the hidden property of UIView and its subclasses.
3) You load a new nib as described above. This is fine for the new nib, but how do you sort the outlets when you go back to the original nib.
4) Give up and accept having a 1:1 between VCs and nibs. There is a nib for normal mode, a nib for edit mode and each mode has a VC that subclasses a common superclass.
In the end, I went with 4) and it works, but requires a fair amount of extra work, because I have a model class that I instantiate in normal mode and then have to pass to the edit mode VC because both modes need access to the model. I'm also using NSTimer and have to start and stop the timer in each mode. It is because of all this shared functionality that I wanted a single VC with 2 nibs in the first place.
I would just add another view to your original nib, and wire it up to another IBOutlet called 'editView'.
#interface TestViewController : UIViewController {
IBOutlet UIView *editView;
}
#end
Then you can can do a [self.view addSubview:theEditView]; whenever you need to show it.

Adding view created in Interface Builder to Progmatically created view - can it be done?

Ok so I've downloaded an open source project that I'm looking at customising for personal use (unless it turns into a great project in which case who knows) but in either case I'm having some difficulty.
From a starting point I'm finding creating UI elements using progmatic methods rather difficult, just can't find a good tutorial out there (if anyone has any suggestions it would be much appreciated!)
So I've come to the conclusion that maybe my best method is to take the view that I wish to modify, recreate it in Interface Builder, then add it to the previous view, the problem here is that I just can't seem to get my head around how to do this. If anyone wants to see the code I can provide it but it's based on the RemotePad open source project (easily googled) and I'm looking at replacing the TapView elements - really all I need to do is add a fourth button under the three mouse buttons but it's just lost me.
I suppose really what I'm asking is what's the best way for me to go about adding this fourth button? Ideally the button should be 'skinable' i.e. should be in the form of an image that can have a highlighted mode applied.
This should actually be fairly easy to do.
First, you need to set up the existing view controller (the one associated with the view where you want to add your button) with an IBOutlet for your new button. So you add something like:
#property (nonatomic, retain) IBOutlet UIButton *myFourthButton;
Next, create the nib file with Interface Builder. Start with an empty IB file and add your button to it. You also need to set the File's Owner to be an instance of the view controller class. Then connect the File Owner's myFourthButton outlet to your new button. Save the IB file.
Now you need some way to load this NIB file when the view controller is created. I would suggest doing this in the view controller's viewDidLoad: method by calling:
[[NSBundle mainBundle] loadNibNamed:#"yourNibFile" owner:self options:nil];
The button from the NIB file should now be connected to your myFourthButton outlet, now you just need to add it to the view and position it. Below I add it to the view controllers main view. However, there may be a subview that you should add it to instead (depends on how the original view is set up). Again, I would put this code in viewDidLoad: after all of the existing code to set up the view programmatically (or in another method if that code is elsewhere).
[self.view addSubview:myFourthButton];
CGRect frame = myFourthButton.frame;
frame.origin.x = 100;
frame.origin.y = 100;
myFourthButton.frame = frame;
When you need your button to actually respond to a tap event, you can connect it to your view controller using an IBAction and Interface Builder just as you would expect.