Im going through the different options of creating a custom UINavigationBar and since my app is iOS 5+, i am using this code:
// Set the background image all UINavigationBars
[[UINavigationBar appearance] setBackgroundImage:NavigationPortraitBackground
forBarMetrics:UIBarMetricsDefault];
Now i want a custom image button on the very right side and am a bit lost. Should I go another way, subclass UINavigationBar and add a button to it or would there be an easier way?
Absolutely subclass this thing. UINavigationBar is easy to subclass, but very hard to put into a navigation controller. I will show you my subclass (CFColoredNavigationBar), which also comes with an arbitrarily colored background for free.
//.h
#import <UIKit/UIKit.h>
#interface CFColoredNavigationBar : UINavigationBar
#property (nonatomic, strong) UIColor *barBackgroundColor;
#property (nonatomic, strong) UIButton *postButton;
#end
//.m
#import "CFColoredNavigationBar.h"
#implementation CFColoredNavigationBar
#synthesize barBackgroundColor = barBackgroundColor_;
#synthesize postButton = postButton_;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(void)awakeFromNib {
postButton_ = [[UIButton alloc]initWithFrame:CGRectMake(CGRectGetWidth(self.frame)-60, 0, 60.0f, CGRectGetHeight(self.frame))];
[postButton_ setImage:[UIImage imageNamed:#"Post.png"] forState:UIControlStateNormal];
[self addSubview:postButton_];
}
- (void)drawRect:(CGRect)rect {
if (barBackgroundColor_ == nil) {
barBackgroundColor_ = [UIColor blackColor];
}
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [barBackgroundColor_ CGColor]);
CGContextFillRect(context, CGRectInset(self.frame, 0, self.frame.size.height*-2));
}
#end
Related
In my testing project, I am planning to have multiple picker views. For these picker views, there will be UIToolbars and custom UIBarButtons. I tried to create a class method so that I will not have to repeat the codes, but it seems like the button is not showing up.
Picker Done Button.h
#import <UIKit/UIKit.h>
#interface UIBarButtonItem (test)
+ (UIBarButtonItem*)barButtonItemWithTint:(UIColor*)color andTitle:(NSString*)itemTitle andTarget:(id)theTarget andSelector:(SEL)selector;
#end
Picker Done Button.m
#import "PickerDoneButton.h"
#implementation UIBarButtonItem (test)
+ (UIBarButtonItem*)barButtonItemWithTint:(UIColor*)color andTitle:(NSString*)itemTitle andTarget:(id)theTarget andSelector:(SEL)selector
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tintColor = color;
button.backgroundColor = [UIColor yellowColor];
[button addTarget:theTarget action:selector forControlEvents:UIControlEventValueChanged];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithCustomView:button];
return doneButton;
}
#end
And to use this class method on my View Controller.m (I am omitting picker view methods which come after viewDidLoad)
#import "ViewController.h"
#import "PickerDoneButton.h"
#interface ViewController ()
#property (strong, nonatomic) NSArray *numberofPeople;
#property (strong, nonatomic) UIPickerView *countPeople;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.countPeople = [[UIPickerView alloc] init];
self.countPeople.dataSource = self;
self.countPeople.delegate = self;
self.numberofPeople = #[#"1",#"2",#"3"];
UIToolbar *countPeopleToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0,0,0,50)];
countPeopleToolbar.barStyle = UIBarStyleBlackOpaque;
// Call the UIBarButtonItem from PickerDoneButton.h
countPeopleToolbar.items = #[[UIBarButtonItem barButtonItemWithTint:[UIColor redColor] andTitle:#"Done" andTarget:self andSelector:#selector(doneClicked)]];
[self pickerView:self.countPeople didSelectRow:0 inComponent:0];
}
Am I using the class method correctly?
Yes you are doing it correct. Only thing you are missing is you are not setting frame of your button to be set inside doneButton. Since button doesn't have any frame set it doesn't show up. Add this after creating button.
button.frame = CGRectMake(0, 0, 30, 30); // change frame as per your requirement
Hi I am adding one custom button programmatically on navigation bar and i have written all button properties in my background class and i am calling this method from my main class
And here I have used protocols for getting button touching event in my main class from background class but it's not working using protocols
my code:-
background class:-
BackGroundClass.h
#import <UIKit/UIKit.h>
#protocol buttonprotocol<NSObject>
#required
- (void) buttonTapped;
#end
#interface BackGroundClass : UIViewController
#property (nonatomic, weak) id<buttonprotocol> delegate;
#end
BackGroundClass.m
#import "BackGroundClass.h"
#interface BackGroundClass ()
#end
#implementation BackGroundClass
#synthesize delegate;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)backbutton: (UINavigationItem *)navigationitem
{
UIImage* image3 = [UIImage imageNamed:#"back.png"];
CGRect frameimg = CGRectMake(15,5,25,25);
UIButton *someButton = [[UIButton alloc] initWithFrame:frameimg];
[someButton setBackgroundImage:image3 forState:UIControlStateNormal];
[someButton addTarget:self action:#selector(Back_btn:)
forControlEvents:UIControlEventTouchUpInside];
[someButton setShowsTouchWhenHighlighted:YES];
UIBarButtonItem *mailbutton =[[UIBarButtonItem alloc] initWithCustomView:someButton];
navigationitem.leftBarButtonItem = mailbutton;
}
- (void)Back_btn :(id)sender
{
[delegate buttonTapped];
}
#end
main class:-
mainclass.h
#import <UIKit/UIKit.h>
#import "BackGroundClass.h"
#interface mainclass : UIViewController<buttonprotocol>
mainclass.m
#import "mainclass.h"
#import "BackGroundClass.h"
#interface mainclass ()
{
BackGroundClass * bg;
}
#end
#implementation mainclass
- (void)viewDidLoad {
[super viewDidLoad];
bg = [[BackGroundClass alloc]init];
[bg backbutton:self.navigationItem];
}
- (void)buttonTapped
{
NSLog(#"ok it's 2");
}
but that above button tapped a method isn't called what I did wrong?
When you are creating the object of your BackGroundClass class, then you are not setting the delegate of the class to self, thats why your delegate method is not calling, try it like this
bg = [[BackGroundClass alloc]init];
bg.delegate = self;
First of all, using Class in class names is bad practice. As for View Controllers you should use either ViewController or VC postfix for your class names.
For example, the proper names of your Objective-C classes would be: BackgroundViewController, MainViewController
Secondly, ViewController instances are used to react to user interaction with the view attached to this ViewController and provide visible changes based on Data component of MVC work.
So, there is no reason to use second, in your case BackgroundViewController (BackgroundClass).
For more info about MVC pattern, please refer this link:
Apple's MVC articles
As there is no need in external ViewController we also should remove the delegate using and place all logic inside this main View Controller.
Thirdly, as mentioned in Apple's Mobile HIG, buttons should have a height around 44px, so your back button's size of 25x25 would make most users experience hard trying to tap on it.
At last, you should receive the class with code similar to it:
MainViewController.h
#import <UIKit/UIKit.h>
#interface MainViewController : UIViewController
#end
MainViewController.m
#import "MainViewController.h"
#implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIImage *icon = [UIImage imageNamed:#"back.png"];
static CGFloat defaultButtonSize = 44.; // Default button's tap area size
CGSize buttonSize = CGSizeMake(defaultButtonSize, defaultButtonSize);
// The size image should take inside the button
CGSize imageSizeInsideButton = CGSizeMake(25., 25.);
CGRect frameimg = CGRectMake(15., 5., defaultButtonSize, defaultButtonSize);
UIButton *someButton = [[UIButton alloc] initWithFrame:frameimg];
[someButton setImage:icon forState:UIControlStateNormal];
[someButton addTarget:self
// No need to put colon after method name,
// since you have no use of control that triggered action
action:#selector(backButtonClicked)
forControlEvents:UIControlEventTouchUpInside];
someButton.showsTouchWhenHighlighted = YES;
// Calculate edge insets so image will take the size
// you specified dozen of lines before
CGFloat imageVerticalPaddingInButton = (buttonSize.height - imageSizeInsideButton.height) / 2.;
CGFloat imageHorizontalPaddingInButton = (buttonSize.height - imageSizeInsideButton.height) / 2.;
// Apply it to button
someButton.imageEdgeInsets = UIEdgeInsetsMake(imageVerticalPaddingInButton,
imageHorizontalPaddingInButton,
imageVerticalPaddingInButton,
imageHorizontalPaddingInButton);
UIBarButtonItem *mailbutton =[[UIBarButtonItem alloc] initWithCustomView:someButton];
self.navigationItem.leftBarButtonItem = mailbutton;
}
// As stated in "addTarget", there is no "sender" since you don't really need it
// If you ever would need the use of it,
// don't forget to put colon in "addTarget" UIButton's method
// and place something like :(UIButton *)buttonThatClicked
// after methodName
- (void)backButtonClicked {
// Get back to your previous view controller or do whatever you like there
[self.navigationController popViewControllerAnimated:YES];
}
#end
But if you want to use this thing in as many View Controllers as possible, then you should create the category:
UIViewController+CustomBackButton.h
#import <UIKit/UIKit.h>
#interface UIViewController (CustomBackButton)
- (void)setBackButtonImage:(UIImage *)image withSize:(CGSize)size target:(id)target selector:(SEL)selector;
#end
UIViewController+CustomBackButton.m
#import "UIViewController+CustomBackButton.h"
#implementation UIViewController (CustomBackButton)
- (void)setBackButtonImage:(UIImage *)image withSize:(CGSize)size target:(id)target selector:(SEL)selector {
static CGFloat defaultButtonSize = 44.;
CGSize buttonSize = CGSizeMake(defaultButtonSize, defaultButtonSize);
CGRect frameimg = CGRectMake(15., 5., defaultButtonSize, defaultButtonSize);
UIButton *someButton = [[UIButton alloc] initWithFrame:frameimg];
[someButton setImage:image forState:UIControlStateNormal];
[someButton addTarget:target
action:selector
forControlEvents:UIControlEventTouchUpInside];
someButton.showsTouchWhenHighlighted = YES;
CGFloat imageVerticalPaddingInButton = (buttonSize.height - size.height) / 2.;
CGFloat imageHorizontalPaddingInButton = (buttonSize.height - size.height) / 2.;
someButton.imageEdgeInsets = UIEdgeInsetsMake(imageVerticalPaddingInButton,
imageHorizontalPaddingInButton,
imageVerticalPaddingInButton,
imageHorizontalPaddingInButton);
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithCustomView:someButton];
self.navigationItem.leftBarButtonItem = backButton;
}
#end
And then your View Controller's implementation file will be much cleaner.
MainViewController.m
#import "MainViewController.h"
#import "UIViewController+CustomBackButton.h"
#implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIImage *icon = [UIImage imageNamed:#"back.png"];
CGSize iconSize = CGSizeMake(25., 25.);
[self setBackButtonImage:icon
withSize:iconSize
target:self
selector:#selector(backButtonClicked)];
}
- (void)backButtonClicked {
// Get back to your previous view controller or do whatever you like there
[self.navigationController popViewControllerAnimated:YES];
}
#end
And then you can use this on every ViewController to customize your back button.
I've looked at many posts and none seem to help with the current issue I'm experiencing. I am trying to change the UIView background color and UILabel text color programmatically on a button click. They are all connected in the interface builder. What I did was open the ViewController.h file next to the Main.storyboard and clicked and dragged to connect the view (backgroundView) and label (letterR).
If anyone can see what I am doing wrong that would be great thanks!
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController{
IBOutlet UIView *backgroundView;
IBOutlet UILabel *letterR;
}
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
static int flag = 0;
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction)ButtonClick:(id)sender {
if(flag == 0){
flag = 1;
letterR.textColor = [UIColor blackColor];
backgroundView.backgroundColor = [UIColor whiteColor];
}
else if(flag == 1){
flag = 0;
letterR.textColor = [UIColor whiteColor];
backgroundView.backgroundColor = [UIColor colorWithRed:0 green:1 blue:1 alpha:1];
}
}
#end
This is firstPopoverViewController.h code:
#import <UIKit/UIKit.h>
#interface firstPopoverViewController : UIViewController
#end
This is my firstPopoverViewController.m code:
#import "firstPopoverViewController.h"
#interface firstPopoverViewController ()
#end
#implementation firstPopoverViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.contentSizeForViewInPopover = CGSizeMake(300, 290);
// Header label
UILabel *h1 = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, 85)];
h1.font = [UIFont fontWithName:#"myFont" size:22.0];
h1.textColor = [UIColor blackColor];
h1.textAlignment = NSTextAlignmentCenter;
h1.text = #"Heading";
h1.numberOfLines = 0;
h1.backgroundColor = [UIColor greenColor];
// Ok button BG View
UIView *buttonBG = [[UIView alloc] initWithFrame:CGRectMake(0, 300-75, 300, 75)];
buttonBG.backgroundColor = [UIColor greenColor];
// Ok button
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(300/2-130/2, 290-35-15, 130, 35);
button.backgroundColor = [UIColor whiteColor];
[button setTitle:#"OK" forState:UIControlStateNormal];
[button addTarget:self action:#selector(closePop) forControlEvents:UIControlEventTouchUpInside];
button.adjustsImageWhenHighlighted=YES;
// Adding views
[self.view addSubview:h1];
[self.view addSubview:buttonBG];
[self.view addSubview:button];
}
-(void)closePop {
}
#end
Then there's ViewController.h:
#import <UIKit/UIKit.h>
#import "firstPopoverViewController.h"
#interface ViewController : UIViewController
#property (strong, nonatomic) UIButton *popButton;
#property (strong, nonatomic) firstPopoverViewController *firstPopoverViewController;
#property (strong, nonatomic) UIPopoverController *firstPopover;
#end
And finally ViewController.m:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
/* ##### UIPopController stuff ##### */
UIImage *popButtonImage = [UIImage imageNamed:#"menu.png"];
_popButton = [UIButton buttonWithType:UIButtonTypeCustom];
_popButton.frame = CGRectMake(0, 0, 73, 66);
[_popButton addTarget:self action:#selector(openPop) forControlEvents:UIControlEventTouchUpInside];
_popButton.adjustsImageWhenHighlighted=NO;
[_popButton setImage:popButtonImage forState:UIControlStateNormal];
[self.view addSubview:_popButton];
}
-(void)openPop {
if (_firstPopoverViewController == nil) {
//Create the _firstPopoverViewController.
_firstPopoverViewController = [[firstPopoverViewController alloc] init];
}
if (_firstPopover == nil) {
_firstPopover = [[UIPopoverController alloc] initWithContentViewController:_firstPopoverViewController];
_firstPopover.popoverContentSize = CGSizeMake(300, 290);
[_firstPopover presentPopoverFromRect:CGRectMake(0,0, 73, 66) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionRight animated:YES];
NSLog(#"show");
} else {
NSLog(#"dismiss");
[_firstPopover dismissPopoverAnimated:YES];
_firstPopover = nil;
}
}
#end
It's pretty basic code that displays a button and when I click this button it shows popover. I want to close this popover using button that's inside firstPopoverViewControll.m file. There's a closePop{} method, what should I put inside it to close this popover? Thanks.
By the way I'm beginner as you can see, I researched stackoverflow and there are some solutions with delegates, which seems to be working for others, but didn't work for me, could you please show me a solution on my code that I posted? Thank you people very much.
There may be a simpler method that I'm not aware of, but the following method should work:
Use NSNotificationCenter to post a notification back to the ViewController containing the UIPopOverController to tell it to dismiss the popover.
First, in ViewController.m viewDidLoad add:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(closePop:) name:#"ClosePopOver" object:nil];
Then add the following method to ViewController.m:
- (void)closePop:(NSNotification *)notification {
[_firstPopover dismissPopoverAnimated:YES];
}
Then in irstPopoverViewController.m:
- (void)closePop {
[[NSNotificationCenter defaultCenter] postNotificationName:#"ClosePopOver" object:nil];
}
That should do the trick.
A delegate is the way to go. I do admit though that I was confused by them at first, but they are quite simple to setup.
In your firstPopoverController.h put this:
#import <UIKit/UIKit.h>
#protocol FirstPopoverDelegate
- (void) closedPopover;
#end
#interface firstPopoverViewController : UIViewController
#property (nonatomic, assign) id< FirstPopoverDelegate > delegate;
#end
Then in your .m of you popover,
-(void)closePop
{
[self.delegate closedPopover];
}
In your main UIViewController's .h:
#import <UIKit/UIKit.h>
#import "firstPopoverViewController.h"
#interface ViewController : UIViewController <FirstPopoverDelegate>
#property (strong, nonatomic) UIButton *popButton;
#property (strong, nonatomic) firstPopoverViewController *firstPopoverViewController;
#property (strong, nonatomic) UIPopoverController *firstPopover;
#end
Then in the .m, first register to listen of the delegate by adding this to your openPop method:
this is important and easy to forget.. nothing will happen if it is not set
_firstPopoverViewController.delegate = self;
Finally, in your .m add the delegate method:
- (void)closedPopover
{
//you can also pass data back in this function, just modify its parameters here and when you define it in the .h of the popover
[_firstPopoverViewController dismissPopoverAnimated:YES];
}
Simple code is here:
Custom MyButton.h
#interface PaiLifeReadingListButton : UIButton
#property (strong, nonatomic) UIImageView *logoImageView;
#end
Custom MyButton.m
#implementation PaiLifeReadingListButton
#synthesize logoImageView = _logoImageView;
-(id)init
{
_logoImageView = [[UIImageView alloc] initWithFrame:CGRectMake(33.0, 20.0, 80.0, 80.0)];
[_logoImageView setImage:[UIImage imageNamed:#"pic"]];
[self addSubview: _logoImageView];
}
#end
Custom MyView.h:
#property (strong, nonatomic) Mybutton *mybutton;
Custom MyView.m:
-(id) init
{
myButton = [[MyButton alloc] init];
[self addSubview:myButton];
}
ViewController.m:
#synthesize myView;
- (void)viewDidLoad
{
myView = [[Myview alloc] init];
[myView.myButton addTarget:self action:#selector(pressed:) forControlEvents:UIControlEventTouchDown];
self.view = myView;
}
-(void)pressed : (MyButton *)button
{
UIImageView *newImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"other-pic"] highlightedImage:nil];
myView.myButton.logImageView = newImageView;
[self.view setNeedsDisplay]; //----this does not work
}
When I press the button, the imageview does not change. How can I fix this?
Thank you very much!
First thing , you don't need setNeedsDisplay here. setNeedsDisplay is used for redrawing the view (it internally calls the drawRect: method of the view if required) which is not what you want.
Second, you have given the class of your button as MyButton,but in the actual .h and .m files, it is given as #interface PaiLifeReadingListButton and #implementation PaiLifeReadingListButton respectively.
Another thing, the view and button is not initialized with a frame. But I assume you have done that already, since your only problem is the image view not changing.
In your code in pressed method you should change views in the hierarchy, but you change only the object member. You can do something like
myView.myButton.logImageView.image = [UIImage imageNamed:#"other-pic"];