How can i make a UITabbar like this? - cocoa-touch

I would like to create a UITabbar like below but i don't know what is the logic to do that.

Here is the large answer:
First of all, you will need to create a UIView subclass to get a view that looks like the bar that you want. It can be composed by a background UIImageView and three buttons.
Then, the best thing would be to create a subclass of the UITabBarController and in its viewDidLoad or at any point where the flow will go through just once, you instantiate one view of type specified at first point. You should place the frame of this view in order to hide the original tabbar of the controller.
This would be the custom bar header file:
#interface CustomBar : UIView
{
}
#property (nonatomic, retain) UIImageView *backgroundView;
#property (nonatomic, retain) NSArray *buttons;
#end
You can easily complete the implementation. You can try to look for how to instantiate it with a nib file to make it easier to design it. In order to test, you can first just set the background color to green or something visible.
Then, this would be the subclass of the UITabBarController class:
#interface CustomTabBarController : UITabBarController
#property (nonatomic, retain) CustomBar *customBar;
#end
#implementation CustomTabBarController
- (void)viewDidLoad
{
[super viewDidLoad];
self.customBar = [[[CustomBar alloc] initWithFrame:[self.tabBar frame]] autorelease];
[self.view addSubview:self.customBar];
}
#end
Please, remember to implement the dealloc if you are not using ARC.
The thing I am not sorting out here is how to create the communication between the buttons from the custombar and the tabbarcontroller. This should be solved by delegates. If you need help with that, I will complete that too.
Good luck!

Related

Loading XIB file without a UIViewController

I'd like to design a UIView and some sub-views (UIWebView, UIToolbar, some UIBarButtonItems, a progress indicator and so-forth) using the Interface Builder, but I think it's unnecessary to do this traditionally, by using a UIViewController, using presentViewController:animated etc.
So, I created a custom class, with the .h file code as follows:
#interface FileInteractionManager : NSObject {
}
#property (nonatomic, retain) IBOutlet UIView *fileView;
#property (nonatomic, retain) IBOutlet UIWebView *fileWebView;
#property (nonatomic, retain) IBOutlet UIBarButtonItem *printButton;
#property (nonatomic, retain) IBOutlet UIBarButtonItem *optionsButton;
#property (nonatomic, retain) IBOutlet UIBarButtonItem *doneButton;
My .m file is as follows:
#implementation FileInteractionManager
#synthesize fileView, fileWebView, doneButton, optionsButton, printButton;
-(id)init {
NSArray *array = [[NSBundle mainBundle] loadNibNamed:#"FileInteractionView" owner:self options:nil];
NSLog(#"Load success!");
return self;
}
Finally, I create a stand-alone xib file named 'FileInteractionView.xib', change the file's owner to the custom class I created above, and wire up the IBOutlets.
When I call the init method on my class, I can see in the debugger that all my IBOutlet objects are instantiated properly.
My questions are:
Is the loadNibNamed:owner:options: method the right way to load my stand-alone .xib file? I don't like the fact that this method returns an array I have no use for (the top-level object returned matches my variable fileView, but I've already linked them through the Interface Builder).
Is my general approach correct in solving my problem? I carried out the above steps because I wanted a simple UIView object that I could add to my existing UIViewController, rather than present and dismiss a whole new UIViewController.
I use a little different approach. I create a subclass of UIView (MyCustomView i.e.) then the xib with the UI of the view and change the (main) view class the the one just defined. In the xib then you can link the outlet to the custom view itself (not the file owner).
Finally in the class definition I create a function like this:
+ (id) newFromNib
{
NSArray *nibArray = [[UINib nibWithNibName:NSStringFromClass([self class]) bundle:nil] instantiateWithOwner:nil options:nil];
return nibArray[0];
}
Just a couple of notes:
1) this's a class method, you can use "self" just for stuff like "NSStringFromClass([self class])" but the real object is the variable returned
2) this example suppose the xib have the same name of the class (via NSStringFromClass([self class]) so I can copy-paste it without changing anything ;) ) and that your view is the first one defined in the xib (the standard). If you store more than a "main" view inside one xib pick the right element.
so where I need MyCustomView I do something like:
MyCustomView* mycv = [MyCustomView newFromNib];
then set frame/center and add to superview...
I think this way is pretty usefull if you have a "library" of complex UI elements and want to design them via xib then add when needed.
il Malvagio Dottor Prosciutto answer is nice. Here is a possible alternative.
Load nib in NS_DESIGNATED_INITIALIZER and become owner of subview
If we accept the xib to only hold a subview instead of the view itself, then we can load the subview in initWithFrame: and keep an ownership construction in xib.
#interface MyCustomView ()
#property (strong, nonatomic) IBOutlet UIView *subview;
#end
#implementation MyCustomView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
[self addSubview:self.subview];
return self;
}
#end

Change ViewControllers & Pass Data in iOS

I'm trying to have my app take a picture then pass the image onto another view for editing but I can't seem to figure out how to change views, how to add an "ID" to a view in the storyboard or how to pass data between views.
The communication between two UIViewControllers needs to be managed manually, however, if you are using storyboards to create your app, there's some things that you need to take into account.
Let's say you have FirstViewController and SecondViewController(Lets assume you have everything set up in your Storyboard). FirstViewController will pass a UIImage to SecondViewController and they will look something like this.
#interface FirstViewController : UIViewController
- (IBAction)transitionToNextViewController;
#property (retain, nonatomic) UIImage *image;
#end
#implementation FirstViewContoller
- (IBAction)transitionToNextViewController;
{
[self performSegueWithIdentifier:#"SegueIdentifier"];
}
#end
And:
#interface SecondViewController : UIViewController
#property (retain, nonatomic) UIImage *image;
#end
You are probably wondering how are you supposed to pass the image to the SecondViewController. Well, when using storyboards, your UIViewControllers will receive a call to their method prepareForSegue:sender: . All you have to do is set the image property for the second UIViewController there.
#implementation FirstViewController
- (IBAction)transitionToNextViewController;
{
[self performSegueWithIdentifier:#"SegueIdentifier"];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
SecondViewController *secondViewController = (SecondViewController *)segue.destinationViewController; // You have to cast it
secondViewController.image = self.image;
}
#end
and that's it. To better understand Storyboards please read the apple docs here.

Modifying properties of a view controller form another view controller

In my project, there are two view controllers - let's say firstViewController and secondViewController. The second view controller has a button, and I want to make sure when the button gets pressed, the second view controller is telling somehow the first view controller - "hey, I got pressed, do something!", and it will do something, like changing a label. How is this possible to perform? Thanks in advance. Some code :
#interface firstViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *textLabel;
#end
#implementation firstViewController
#synthesize textLabel;
#end
#interface secondViewController : UIViewController
-(IBAction)buttonPressed;
#end
#implementation secondViewController : UIViewController
-(IBAction)buttonPressed{
// Hey, I got pressed! Set the text on textLabel to "OK"!
}
#end
This is a very simple case of delegation and protocol mechanism of objective-c..
have a look at this tutorial which will explain you how its done.. you can do this via notification also but that is not usually advised...(because notification is usually used when the receiver is unknown , like in the case of UIDeviceBatteryLevelDidChangeNotification you don't exactly know which view controller wants to know about this.)
I'd first consider what the button press means. Does it change the state of the model?
Say your model is an int, and the button increments it. The view controllers wouldn't message each other about that, they would just both observe the state of the model. (The one with the button could change the state, too).
Thinking about it this way, the solution probably isn't delegation. It's probably notification or KVO.
See the answer to this question: Passing data between two view controllers via a protocol
However, ask yourself if you really need a protocol here. If it is just between this classes or just about the question of accessing data of a class or sending information to a class then that is what the interface of a class is made for.
#interface firstViewController : UIViewController{
UILabel *textLabel; // I personally alway add IBOutlet here too, but I think that is not required.
}
#property (weak, nonatomic) IBOutlet UILabel *textLabel;
#end
And in SecondViewController.m:
#import "FirstViewController.h"
#implementation secondViewController : UIViewController
-(IBAction)buttonPressed{
// You will have to have a properly set instance variable firstViewController
[firstViewController.textLabel setText:#"OK"];
}
#end
So your second view controller needs to 'know' the first one. One way of achieving that is defining
FirstViewController *firstViewController;
as property and set it from wherever the second view controller is created and the first one is already known. How to do that exactly depends very much on the architecture of your app.

Setting the initial value of a UILABEL

I'm trying to create a simple Quiz app (I'm a beginner), when I launch the app I want a UILabel to show the first question (of an array of questions). I'm having some trouble with setting the initial value.
I've done a couple of attempts, whiteout success. I my QuizAppDelegate.h file I declare my UILabel like this:
IBOutlet UILabel * questionField;
In my main .m file I have tried the following:
- (id)init {
[super init];
questions = [[NSMutableArray alloc] init];
// Not working
questionField = [[UILabel alloc] init];
[questionField setText:#"Hello"];
// Working
NSLog(#"Hello");
[self defaultQuestions];
// [self showQuestion];
return self;
}
Another thing I have tried is the following in QuizAppDelegate:
#property (nonatomic, retain) IBOutlet UILabel *questionField;
- (void)changeTitle:(NSString *)toName;
And in the .m file:
#synthesize questionField;
- (id)init {
[super init];
questions = [[NSMutableArray alloc] init];
// Not working
[self changeTitle:#"Hello"];
// Working
NSLog(#"Hello");
[self defaultQuestions];
// [self showQuestion];
return self;
}
-(void)changeTitle:(NSString *)toName {
[questionField setText:toName];
}
Any tips on how to solve this would be great!
// Anders
Hopefully you're not actually putting code into main.m. On iOS, you rarely modify that file.
Since you're doing everything in the AppDelegate, let's keep it there (as opposed to creating a new UIViewController). Let's start with the basics.
Adding the Label as an instance variable
You're doing this correctly—inside the curly braces of the .h file, put the line
IBOutlet UILabel * questionField;
Then, declare the corresponding property, and make sure to synthesize it in the .m file.
#property (nonatomic, retain) IBOutlet UILabel *questionField;
#synthesize questionField // in the .m file
Adding the UILabel in Interface Builder
Open up MainWindow.xib. Drag a UILabel from the Library to the Window that represents your app's window. Then Control-Drag from the AppDelegate object (the third icon on the left in Xcode 4; it'll be labelled in the Document window in IB 3). You'll see a little black window come up—select the option called questionField to make the connection.
See this link for screenshots and how to make connections in IB. The same applies in Xcode 4.
Changing the text
You don't need a separate method to change the text—just modify the label's text property.
Pick a method that'll be called when the app launches (applicationDidFinishLaunching:WithOptions: is a good place to do it in), and put the following code:
questionField.text = #"Hello";
And that's it!
Code
QuizAppDelegate.h
#import <UIKit/UIKit.h>
#interface QuizAppDelegate : NSObject <UIApplicationDelegate> {
IBOutlet UILabel *questionField;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UILabel *questionField;
#end
QuizAppDelegate.m
#import "QuizAppDelegate.h"
#implementation QuizAppDelegate
#synthesize window=_window;
#synthesize questionField;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Add the tab bar controller's current view as a subview of the window
[self.window addSubview:self.questionField];
[self.window makeKeyAndVisible];
self.questionField.text = #"Hello";
return YES;
}
- (void)dealloc
{
[_window release];
[questionField release];
[super dealloc];
}
#end
If you're creating the label programmatically, then you have to add the label to the view:
[self.view addSubview:questionField];
This assumes that you have a ViewController. If not, and you're doing this directly in the AppDelegate (a very bad idea, by the way), then do
[self.window addSubview:questionField];
If you're creating it in the IB, make sure you set up the connections.
You should not both add the UILabel in the IB and instantiate it programmatically. Only call alloc if you are creating it programmatically. Otherwise, if using the IB, skip that part. You created it already with the xib.
I suspect that you have either not created your Interface Builder layout properly - either you have missed the control out all together or more likely you have not connected that control to the questionField outlet in yout header file.
You need to drag a UILabel view into the main view and then connect it to the correct line in your header file.
You shouldn't be using your main.m like that at all. In fact, you should almost certainly never do anything with it. Try creating a UIViewController subclass and practicing your quiz with that. (Add the UILabel to the IB file and then connect the outlet.) Perhaps use the View-Based Application template while you are practicing.
This is a good answer:
"You're doing this correctly—inside the curly braces of the .h file, put the line
IBOutlet UILabel * questionField;"
I was trying to change the value of mylabel.text and the screen didn't update the label with this.value. I included the {IBOutlet UILabel * mylabel} and it works like a charm!
So this answer is valid to change the text of a label programmatically!
Thanks

Set delegate to parent view controller

I have three view controllers in my app pushed to the navigation controller. When I'm on the third view controller I want to send a message to the first one. I think a delegate is the way to go here, but I'm not able to set it correctly.
In my third view controller's h-file I have this:
#protocol AddSudokusViewControllerDelegate
- (void)saveSudoku:(Sudoku *)sudoku;
#end
#interface [...]
id<AddSudokusViewControllerDelegate> delegate;
[...]
#property (nonatomic, retain) id<AddSudokusViewControllerDelegate> delegate;
Then I synthesize it in the .m file.
In my first view controller I have this (.h):
#interface SudokusViewController : UITableViewController <AddSudokusViewControllerDelegate>{
[...]
}
- (void)saveSudoku:(Sudoku *)sudoku;
So far so good I think. Now I want to set the delegate of the third view controller to first when I create it in the second controller. I thought I could do it like this, but it doesn't work.
sudokuDetailViewController = [[SudokuDetailViewController alloc] init];
[sudokuDetailViewController setDelegate:[[self navigationController] parentViewController]];
[[self navigationController] pushViewController:sudokuDetailViewController animated:YES];
Am I doing this the right way or is there another approach when you have three controllers like this?
Best Regards
Linus
Try this,
sudokuDetailViewController.delegate = [self.navigationController.viewControllers objectAtIndex:0];