I created a .xib file form my custom view.
I created .h/.m files for that view.
I ctrl dragged from button to header file to create an IBAction and set the value to touchUpInside. Here is what is happening:
http://screencast.com/t/R1WTpK7xp
WTF?
It triggers event when up is outside the button?
EDIT:
Here is the screenshot:
And what is the thing with down vote? I don't see a point in that.
View.h
#import <UIKit/UIKit.h>
#import "DrawingViewDelegate.h"
#interface DrawingBottomToolbarView : UIView
#property (weak) id <DrawingViewDelegate> delegate;
- (IBAction)lineSegmentButtonPush:(id)sender;
#end
View.m
#import "DrawingBottomToolbarView.h"
#implementation DrawingBottomToolbarView
#synthesize delegate;
- (id)initWithFrame:(CGRect)frame
{
NSLog(#"frame");
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self addSubview:[[[NSBundle mainBundle] loadNibNamed:#"DrawingBottomToolbarView" owner:self options:nil] objectAtIndex:0]];
//[[[NSBundle mainBundle] loadNibNamed:#"DrawingBottomToolbarView" owner:self options:nil] objectAtIndex:0];
//[self addSubview:self.];
}
return self;
}
//-(id)initWithCoder:(NSCoder *)aDecoder{
//
// NSLog(#"coder");
// if ((self = [super initWithCoder:aDecoder])){
// [self addSubview:[[[NSBundle mainBundle] loadNibNamed:#"DrawingBottomToolbarView" owner:self options:nil] objectAtIndex:0]];
// }
// return self;
//}
- (IBAction)lineSegmentButtonPush:(id)sender
{
NSLog(#"line push");
}
#end
I don't get it where is the problem.
EDIT 2:
I tried setting buttons as outlets and add target/action in code and same thing happens:
.h
#property (weak, nonatomic) IBOutlet UIButton *lineSegmentButton;
.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self addSubview:[[[NSBundle mainBundle] loadNibNamed:#"DrawingBottomToolbarView" owner:self options:nil] objectAtIndex:0]];
self.currentSelectedPathSegment = NoneSegment;
[self.lineSegmentButton addTarget:self action:#selector(lineSegmentButtonPush:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
EDIT 3: Here is where I add two views. drawing view is created in code, bottomToolbar is created from .xib file.
kBottomToolbarHeight is constant with same value as height defined in .xib file.
- (void)viewWillAppear:(BOOL)animated
{
[self.view addSubview:self.drawingView];
[self.view addSubview:self.bottomToolbar];
CGRect selfRect = self.view.bounds;
CGRect drawingViewRect = selfRect;
CGRect bottomToobarRect = selfRect;
drawingViewRect.size.height = selfRect.size.height - kBottomToolbarHeight;
bottomToobarRect.size.height = kBottomToolbarHeight;
bottomToobarRect.origin.y = drawingViewRect.size.height;
self.drawingView.frame = drawingViewRect;
self.bottomToolbar.frame = bottomToobarRect;
}
The behavior you mention and show in the screencast is exactly the same as I experienced when I somewhere in the view hierarchy had a parent view with a UITapGestureRecognizer.
I'm unsure whether or not to flag your question as a possible duplicate of this one which helped me solve my problem.
For reference this is not a problem in iOS 6.0, only earlier versions.
Related
I have a custom view called TimeTooltipView. Here is the code:
TimeTooltipView.h
#import <UIKit/UIKit.h>
#interface TimeTooltipView : UIView
#property (weak, nonatomic) IBOutlet UILabel *timeLabel;
-(void)configureView;
#end
TimeTooltipView.m
#import "TimeTooltipView.h"
#implementation TimeTooltipView
-(id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:#"TimeTooltipView" owner:self options:nil];
UIView *mainView = [subviewArray objectAtIndex:0];
[self addSubview:mainView];
[self configureView];
}
return self;
}
-(void)configureView {
self.backgroundColor = [UIColor greenColor];
}
#end
I add a TimeTooltipView in a view controller, like so:
TimeTooltipView *timeTooltipView = [[TimeTooltipView alloc]
initWithFrame: CGRectMake(100, 100, 50, 50)];
timeTooltipView.timeLabel.text = #"TEST";
[self.view addSubview:timeTooltipView];
timeTooltipView.timeLabel.text = #"TEST2";
I need to change timeLabel's text on the fly from the view controller. Using the code above, the view gets added and has a green background color. But the label text never changes to "TEST" or "TEST2".
How can I change the custom view's label text on the fly?
-(id)initWithFrame:(CGRect)frame {
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:#"TimeTooltipView" owner:self options:nil];
//Instead of making it subView just replace it.
self = [subviewArray objectAtIndex:0];
self.frame = frame;
[self configureView];
return self;
}
-(void)configureView {
self.backgroundColor = [UIColor greenColor];
}
I have created MyCustomView.xib/h/m: which extends UIView class. Then in my Main.storyboard, put UIView object, changed the class to MyCustomView and linked to MainController.h. So, MainController contains reference to MyCustomView instance.
For loading from xib, in MyCustomView I do the following:
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
if (self.subviews.count == 0) {
[self commonInit];
}
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (void) stretchToSuperView:(UIView*)view
{
view.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *bindings = NSDictionaryOfVariableBindings(view);
NSString *formatTemplate = #"%#:|[view]|";
for (NSString * axis in #[#"H",#"V"]) {
NSString * format = [NSString stringWithFormat:formatTemplate,axis];
NSArray * constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:bindings];
[view.superview addConstraints:constraints];
}
}
- (void)commonInit
{
MyCustomView* view = nil;
NSArray *views = [[NSBundle mainBundle] loadNibNamed:#"MyCustomView" owner:self options:nil];
view = views.firstObject;
[self addSubview:view];
[self stretchToSuperView:views.firstObject];
}
This works quite well, until I want to declare delegate in MyCustomView in order to notify MainController to any change(button click, etc). So, my ManController conforms MyCustomViewDelegate and implements methods.
EDIT 1 setting delegate
//MainViewController.m file
#interface MainViewController ()
#property (strong, nonatomic) IBOutlet MyCustomView *customView;
#end
#implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.customView.delegate = self;
}
The problem here is that delegate becomes nil and I don't understand the reason, so don't know what's the mistake.
Edit 2 I think somehow I have 2 different instances of MyCustomView.
I have added new property in MyCustomView:
#interface MyCustomView : UIView
#property (weak, nonatomic) IBOutlet UIButton *firstItem;
#property(nonatomic, weak)id <MyCustomViewDelegate> delegate;
// this is test property
#property(nonatomic, assign)int testProperty;
#end
And when I set this property in viewDidLoad and then click to first button, I see that testProperty has value 0. So, this could mean something wrong with IBOutlet MyCustomView *customView.
You are correct, that you have two view objects. The one that you added to the storyboard and the one you created via loadNibNamed.
Bottom line, loadNibNamed will create the view for you (assuming that you've specified MyCustomView as the base class for the view specified in the NIB; you can leave NIB's "File owner" blank). You can then write a convenience method in MyCustomView to instantiate the NIB-based view:
+ (instancetype)myCustomViewWithDelegate:(id<MyCustomViewDelegate>)delegate {
NSArray *array = [[NSBundle mainBundle] loadNibNamed:#"MyCustomView" owner:delegate options:nil];
MyCustomView *view = array.firstObject;
NSAssert([view isKindOfClass:[MyCustomView class]], #"Base class of NIB's top level view is not MyCustomView");
view.delegate = delegate;
return view;
}
Then, rather than specifying the view on the storyboard, you must instantiate and add it to the view hierarchy programmatically, e.g.:
- (void)viewDidLoad {
[super viewDidLoad];
MyCustomView *view = [MyCustomView myCustomViewWithDelegate:self]; // since `self.myCustomView` should be `weak`, let's hold the view in local variable
[self.view addSubview:view];
self.myCustomView = view; // then set the property after the view is safely added to view hierarchy
NSDictionary *views = #{#"myCustomView" : self.myCustomView};
self.myCustomView.translatesAutoresizingMaskIntoConstraints = false;
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[myCustomView]|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[myCustomView]|" options:0 metrics:nil views:views]];
// ...
}
Obviously, when adding a view programmatically, you have to specify its frame and autoresizingMask, or use constraints like shown above.
Unfortunately, I can't make a simple comment, so I have to ask, and give advices as 'answer'.
First of all, I don't see where do you set the delegate to your MainController, but I think in the viewDidLoad() of the controller.
Second thing. It's really important how do you setup your MyCustomView.xib, because you create a brand new object MyCustomView, and its properties will be unavailable from the controller.
MyCustomView* view = nil;
NSArray *views = [[NSBundle mainBundle] loadNibNamed:#"MyCustomView" owner:self options:nil];
view = views.firstObject;
[self addSubview:view];
Without knowing the setup of the xib, and setting method of the delegate, here is my guess what could help you:
In MyCustomView.xib set the class of the rootView to UIView.
In MyCustomView.xib set the class of the File's Owner to MyCustomView
Create your subview as a UIView.
UIView* view = nil;
NSArray *views = [[NSBundle mainBundle] loadNibNamed:#"MyCustomView" owner:self options:nil];
view = views.firstObject;
[self addSubview:view];
Set the delegate of the myCustomView object in your controller.
self.myCustomView.delegate = self;
I hope it will help, if not, then please extend your question with the delegate setter code block, and your xib setup method. (class of file's owner, class of root view etc)
I'm working on as slideshow with some CustomUIView showed in a UIScrollView and it works good. But i have a custom UIView to show the content of each news. My slide works goods , but the XIB file of my custom view is loaded only for the first one and other customview are empty.
Here is my code :
my ViewController.m
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:YES];
int nbrNews = _sharedDataManager.rssfeeds.count;
_sliderView.contentSize = CGSizeMake(self.view.frame.size.width*nbrNews, self.view.frame.size.height);
self.pageControl.currentPage = 0;
self.pageControl.numberOfPages = nbrNews;
for (int i = 0; i< nbrNews; i++) {
NewsView *newsView = [[NewsView alloc] initWithFrame:CGRectMake(i*self.view.frame.size.width, 0, self.view.frame.size.width, self.view.frame.size.height)];
NSDictionary *news = [_sharedDataManager.rssfeeds objectAtIndex:i];
newsView.titleLabel.text = [news objectForKey:#"title"];
[_sliderView addSubview:newsView];
}
}
CustomView.h
#import <UIKit/UIKit.h>
#interface NewsView : UIView
#property (weak, nonatomic) IBOutlet UILabel *titleLabel;
#end
CustomView.m
#import "NewsView.h"
#implementation NewsView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self = [[[NSBundle mainBundle] loadNibNamed:#"OneNewsView" owner:self options:nil] objectAtIndex:0];
}
return self;
}
#end
here the result :
EDITED : i change the color of the view in my custom view. and we can see that only the first one is realy loaded with xib file. and i dont know why !!!! can you help me?
i solve my problem by adding :
self.frame = frame;
to my customView.m initWithFrame
I have a map with the MapBox library.
Now i set the background like this:
UIView *background = [[[NSBundle mainBundle] loadNibNamed:#"IGBackgroundView" owner:self options:nil] objectAtIndex:0];
[_mapView setBackgroundView:myView];
NOTE setBackgroundView: aks for a UIView*
This works great, it shows correct.
What i want is to have the background a bit more fancy.
I want to animate it using:
animationContainer.animationImages = imagesArray;
animationContainer.animationDuration = 0.5f;
[animationContainer startAnimating];
There for i want to make a controller so the nib can have a file owner and there i can add the code to keep things nice and clean.
Now i have:
#import <UIKit/UIKit.h>
#interface IGBackgroundViewController : UIView
#property (nonatomic, strong) UIView *view;
#end
and
#import "IGBackgroundViewController.h"
#implementation IGBackgroundViewController
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
_view = [[[NSBundle mainBundle] loadNibNamed:#"IGBackgroundView" owner:self options:nil] objectAtIndex:0];
[self addSubview:_view];
}
return self;
}
#end
And to set the background i use:
IGBackgroundViewController *background = [[IGBackgroundViewController alloc] init];
[_mapView setBackgroundView:background];
It works but it shows like 25% of the total:
Anyone any idea why this happens?
P.s. i'm pretty new to iOS development.
Pay attention to Autosize!
Probably you have setted something wrong in Interface Builder and when you run the app, the view go in a different position.
hope this help.
I have a problem with a custom uiview (MyCustomView) loading its view from a NIB (MyCustomView.xib).
The problem is the MyCustomView is reused in other XIBs so awakeFromNib is called.
The CustomView.xib also contains an instance of MyCustomView so loadNibNamed in MyCustomView.awakeFromNib goes into infinite loop
im getting infinite loop as:
SomeVC.xib
+ calls MyCustomView awakeFromNib
++ which calls loadNibNamed: MyCustomView.nib
+++++ but top level View is also instance of MyCustomView
....so awakeFromNib: goes into infinite loop
Im just wondering if when a custom view loads its self from a nib then top level UIView shouldnt be instance of MyCustomView?
If so how do I wire up outlet/actions (do I make MyCustomView the files owner instead?)
anyway cheers
LONG EXPLANATION:
I have a row of buttons which I want to reuse on a number of screens.
So I created a custom view
#interface ScreenMenuButtonsView : UIView
#property (retain, nonatomic) IBOutlet UIButton *buttonHome;
#property (retain, nonatomic) IBOutlet UIButton *buttonMyMusic;
#property (retain, nonatomic) IBOutlet UIButton *buttonStore;
#property (retain, nonatomic) IBOutlet UIButton *buttonSocial;
#property (retain, nonatomic) IBOutlet UIButton *buttonSettings;
#end
I wanted to layout the view in IB so I created a NIB
ScreenMenuButtonsView_iPad.xib
Files Owner: no set NSObject
VIEW: ScreenMenuButtonsView
+ UIButton
+ UIButton
+ UIButton
I want the custom view to load its own NIB internally and I used the example from
http://nathanhjones.com/2011/02/20/creating-reusable-uiviews-with-a-drop-shadow-tutorial/
http://nathanhjones.com/wp-content/uploads/2011/02/ReusableTableHeaders.zip
- (id)initWithFrame:(CGRect)frame {
//self = [super initWithFrame:frame]; // not needed - thanks ddickison
if (self) {
NSArray *nib = [[NSBundle mainBundle]
loadNibNamed:#"HeaderView"
owner:self
options:nil];
[self release]; // release object before reassignment to avoid leak - thanks ddickison
self = [nib objectAtIndex:0];
self.layer.shadowColor = [[UIColor blackColor] CGColor];
self.layer.shadowOffset = CGSizeMake(1.0, 1.0);
self.layer.shadowOpacity = 0.40;
}
return self;
}
Which is ok except this presumes you call initWithFrame somewhere to instantiate the CustomView.
The HeaderView example calls it like this:
// load the header - this could be done in viewWillAppear as well
HeaderView *header = [[HeaderView alloc]
initWithFrame:CGRectMake(0, 0, 320, 60)];
header.lblTitle.text = #"That. Just. Happened.";
tblData.tableHeaderView = header;
[header release];
I wanted to be able to use my custom view class in any other XIB
HomeScreenViewController.xib
Files Owner: HomeScreenViewController
View:
+ ....other controls
+ ScreenMenuButtonsView
The problem is when HomeScreenViewController is loaded it calls awakeFromNib on my customView:
so I moved the nib loading out initWithFrame and into a method called setupView and called it from initWithFrame AND awakeFromNib
- (id) initWithFrame:(CGRect)frame
{
if ( ( self = [super initWithFrame: frame] ) )
{
[self setUpView];
}
return self;
}
- (void)awakeFromNib
{
[self setUpView];
}
- (void) setUpView{
if (self) {
NSArray* nibViewsArray = nil;
if([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad){
//IPAD
nibViewsArray = [[NSBundle mainBundle] loadNibNamed:#"ScreenMenuButtonsView_iPad"
owner:self
options:nil];
...
}
My problem is that when HomeScreenViewController.xib is loaded:
HomeScreenViewController.xib
Files Owner: HomeScreenViewController
View:
+ ....other controls
+ ScreenMenuButtonsView
It tries to create ScreenMenuButtonsView and because its in a NIB
it calls
ScreenMenuButtonsView awakeFromNib
BUT in here I call loadNibNamed:
- (void) setUpView{
_viewSetupAlready = TRUE;
//http://nathanhjones.com/2011/02/20/creating-reusable-uiviews-with-a-drop-shadow-tutorial/
//self = [super initWithFrame:frame]; // not needed - thanks ddickison
if (self) {
NSArray* nibViewsArray = nil;
if([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad){
//IPAD
nibViewsArray = [[NSBundle mainBundle] loadNibNamed:#"ScreenMenuButtonsView_iPad"
owner:self
options:nil];
}else{
//IPHONE
nibViewsArray = [[NSBundle mainBundle] loadNibNamed:#"ScreenMenuButtonsView_iPhone"
owner:self
options:nil];
}
if(nibViewsArray){
//[self release]; // release object before reassignment to avoid leak - thanks ddickison
UIView * myView = [nibViewsArray objectAtIndex: 0];
//Check the top level object in NIB is same type as this custom class
//if wrong you probably forgot to use correct NIB name in loadNibNamed:
if([myView isMemberOfClass:[self class]]){
self = (ScreenMenuButtonsView *)myView;
}else{
NSLog(#"ERROR:top level view is not same type as custom class - are you loading the correct nib filename in loadNibNamed:\n%#", myView);
}
}else{
NSLog(#"ERROR:nibViewsArray is nil");
}
}
}
loadNibName: load
ScreenMenuButtonsView_iPad.xib
Files Owner: no set NSObject
VIEW: ScreenMenuButtonsView
+ UIButton
+ UIButton
+ UIButton
But top view is ScreenMenuButtonsView
so creates a new instance and calls awakeFromNib
and I get infinite loop.
I know why the loops occurring and instance on ScreenMenuButtonsView is loading another instance from the xib.
My question is in
ScreenMenuButtonsView_iPad.xib
Files Owner: no set NSObject
VIEW: ScreenMenuButtonsView
+ UIButton
+ UIButton
+ UIButton
should I change
VIEW: UIView
but what about outlets and actions for the button:
should I change
Files Owner: no set NSObject
to be the view class
Files Owner: ScreenMenuButtonsView