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.
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
Related to this previous question (Creating UI elements programmatically in Objective-C), this method resides in one class of the project, and other classes are able to call it to programmatically create buttons:
UIButton *createButton(CGFloat x, CGFloat y, CGFloat width, CGFloat height, NSString *caption, NSTextAlignmentCenter textPosition, UIColor *textColor, UIColor *backColor) {
UIButton *control = [[UIButton alloc] initWithFrame: CGRectMake(x, y, width, height)];
[control setTitle:caption forState:UIControlStateNormal];
control.titleLabel.textAlignment = textPosition;
control.backgroundColor = backColor;
[control setTitleColor: textColor forState:UIControlStateNormal];
return control;
}
I am now trying to add the target and action methods, to be handled by a common function, like so:
[control addTarget:nil
action:#selector(commonHandler)
forControlEvents:UIControlEventTouchDown];
It works for buttons that reside in the same class as the createButton() function, but although the buttons are successfully created in the other classes as well, they don't trigger the commonHandler() method.
NOTE: the createButton() and commonHandler() methods have been declared in its header file, and imported by all classes.
Is there any way to achieve this?
EDIT: The proposed solution still crashes
I modified the code as per the suggestions, and converted both the createButton and buttonAction to Objective-C methods, to enable reference to self.
class1 header
// class1.h
#import <UIKit/UIKit.h>
#import "class2.h"
#interface class1 : UIViewController {
UIButton *button1;
}
- (void) buttonAction:(UIButton *)control;
- (UIButton *) createButtonWithxPos:(CGFloat)x
yPos:(CGFloat)y
width:(CGFloat)width
height:(CGFloat)height
caption:(NSString *)caption
textPos:(NSTextAlignment)textPosition
textClr:(UIColor *)textColor
backClr:(UIColor *)backColor;
#end
class1 implementation
// class1.m
#import "class1.h"
#import "class2.h"
#implementation class1
- (void)viewDidLoad {
[super viewDidLoad];
button1 = [self createButtonWithxPos:10
yPos:10
width:200
height:30
caption:#"Cls1 Button"
textPos:NSTextAlignmentCenter
textClr:[UIColor whiteColor]
backClr:[UIColor blackColor]];
[self.view addSubview: button1];
}
- (UIButton *) createButtonWithxPos:(CGFloat)x
yPos:(CGFloat)y
width:(CGFloat)width
height:(CGFloat)height
caption:(NSString *)caption
textPos:(NSTextAlignment)textPosition
textClr:(UIColor *)textColor
backClr:(UIColor *)backColor
{
UIButton *control = [[UIButton alloc] initWithFrame: CGRectMake(x, y, width, height)];
[control setTitle:caption forState:UIControlStateNormal];
control.titleLabel.textAlignment = textPosition;
control.backgroundColor = backColor;
[control setTitleColor: textColor forState:UIControlStateNormal];
[control addTarget:self action: #selector(buttonAction:) forControlEvents:UIControlEventTouchDown];
return control;
}
- (void) buttonAction:(UIButton *)control {
NSLog(#"button clicked...");
}
#end
class2 header
// class2.h
#import <UIKit/UIKit.h>
#interface class2 : UIViewController {
UIButton *button2;
}
#end
class2 implementation
// class2.m
#import "class1.h"
#import "class2.h"
#implementation class2
- (void) viewDidLoad {
[super viewDidLoad];
class1 *cls1 = [[class1 alloc] init];
button2 = [cls1 createButtonWithxPos:10
yPos:10
width:200
height:30
caption:#"Cls2 Button"
textPos:NSTextAlignmentCenter
textClr:[UIColor whiteColor]
backClr:[UIColor blackColor]];
[self.view addSubview: button2];
}
#end
Both button1 and button2 are created properly, but button1 triggers the buttonAction method while button2 does not - it crashes on touch.
Is it implemented wrongly?
(please excuse any omissions as I have only extracted the relevant portions from the actual project to illustrate here)
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"];
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