Custom Control in iPhone Interface Builder - iphone

This appears to be a common question, but I haven't seen it answered clearly or succinctly - and as such I'm having trouble making it work.
I have created a Widget.xib file in Interface Builder. It consists of:
View (UIView)
ImageView (UIImageView)
Button (UIButton)
Label (UILabel)
I have created Widget.h and Widget.m, which implement class Widget, which derives from UIView. In Interface Builder, Widget.xib has its File's Owner set to the Widget class.
Widget.h looks like this:
#interface IconView : UIView {
IBOutlet UIButton *widgetButton;
IBOutlet UILabel *widgetLabel;
IBOutlet UIView *widgetView;
IBOutlet UIImageView *widgetImage;
}
#property (nonatomic, retain) IBOutlet UIButton *widgetButton;
#property (nonatomic, retain) IBOutlet UILabel *widgetLabel;
#property (nonatomic, retain) IBOutlet UIView *widgetView;
#property (nonatomic, retain) IBOutlet UIImageView *widgetImage;
Widget.h includes:
#synthesize widgetButton, widgetLabel, widgetView, widgetImage;
And its dealloc releases those four objects. In Interface Builder, the button, label, view, and imageview are all connected to the appropriate IBOutlets.
Back in Interface Builder, I've opened another XIB from the project. This is Playfield.xib. It's a more traditional view, backed with PlayfieldViewController.h and .m. It contains:
View (UIView)
ImageView (with a static graphic) (UIImageView)
Button (UIButton)
Widget
Widget
Widget
In Playfield.h, I've created IBOutlets for the button and the three Widgets.
#interface LinkViewController : UIViewController {
IBOutlet UIButton *buttonBack;
IBOutlet Widget *widget0;
IBOutlet Widget *widget1;
IBOutlet Widget *widget2;
}
#property (nonatomic, retain) IBOutlet Widget *widget0;
#property (nonatomic, retain) IBOutlet Widget *widget1;
#property (nonatomic, retain) IBOutlet Widget *widget2;
- (IBAction)buttonBackClicked:(id)sender;
These are also synthesized and released in Playfield.h. In Interface Builder, Playfield.xib shows three empty rectangles where the widgets have been placed, which is what I expected. Those are wired up to their corresponding IBOutlets in Playfield.h.
So, at this point I assume everything is ready to go. In Playfield.m's viewDidLoad, I do this:
self.widget0.label.text = #"Feed Me";
In my mind, that references the widget0 IBOutlet declared in Playfield.h. That connects to the first Widget item in Playfield.xib. That, in turn, has a declared label IBOutlet in Widget.xib and Widget.h. A UILabel has a Text property. So it should change the text on the label within the widget.
But it doesn't. What am I doing wrong?
(I want to emphasize that I'm not looking to make an Interface Builder plugin. I know those are complicated. Within IB, I'm fine manipulating my Widgets as empty rectangles. I'll also note that I've set the background color for the widget's View, in Widget.xib, to white. When I run the project, I can clearly see the white rectangles atop the playfield graphic, so I know the widgets are "in" there. I just can't seem to access them.)

Where did you call the loadNibNamed:owner:options: method to load your Widget subviews from your Widget.XIB?
Also you did connect your views in your Widget.XIB file to the File's Owner (which is a Widget object) so the IBOutlets are connected and give you access to the views… but did you add the views as a subview to your Widgets?
Is the outlet nil when you try to access it using self.widget0.label ? Check if self.widget0 is not nil (I guess for that it is OK) but also if self.widget0.label is not nil too (I guess that's the real the problem). If it is, you probably forgot to load your Widget.xib for each widget view. If it isn't, then it probably means that you have your label, but it isn't added to the view hierarchy.

Your description of your XIB setup doesn’t mention the label outlet on Widget being actually connected to its label in the XIB. Check that, maybe? It sounds as if you’ve got everything else set up, though you don’t need to declare the IBOutlet attribute on the instance variable, just the property.

A common one I often miss is setting the class type to widget in IB.

Related

No visible #interface for UIButton declares the selector"setbookmarkState

Declared BOOL
#property (nonatomic, assign) BOOL BookmarkState;
When using the BOOL for UIButton added on navigationbar using storyboard
[_bookmarkbtn setBookmarkState:NO];
then gets ARC Semantic issue
No visible #interface for UIButton declares the selector"setbookmarkState
EDIT:
PageViewController h file
#interface PageViewController : UIViewController <UIPageViewControllerDelegate, UIPageViewControllerDataSource>
#property (nonatomic, assign) BOOL BookmarkState;
#end
PageViewController m file
#interface PageViewController () {
BOOL BookmarkState;
}
#property (strong, nonatomic) IBOutlet UIButton *bookmarkbtn;
#implementation PageViewController
UIButton has no property called bookmarkState.
If you want to disable your button bookmarkbtn, use [_bookmarkbtn setEnabled:NO];
Or if you want to deselect it, use [_bookmarkbtn setSelected:NO];
Or if you want to unhighlight it, use [_bookmarkbtn setHighlighted:NO];
If you want to set the bookmarkState to NO, use [self setBookmarkState:NO];
Edit: it seems like you've named the booleans differently (one with lowercase, and one with uppercase) so my question is - are you certain of what are you doing / of what are you trying to accomplish?
Pardon me if it will sound rude or disrespectful, but calling setbookmarkState on UIButton is clearly showing that you aren't quite sure of how to do something you that you need.
Also, XCode should give you an error stating that no visible interface for UIButton declares the selector setbookmbarState:
First of all - as stated, UIButton has no property bookmarkState.
Then, calling an autosynthesized setter method always implies that you use setMyProperty, not setmyProperty, or setmyproperty. It's weird how you got it to run :)
Good luck!

iOS 6 issue during setting text on a label

I've a problem with some label. I made a public method that takes information from a table view and shows this info in 3 label, a text view and it load a pic from the web. I setup all the variable in the table view didSelectRowAtIndexPath method, but I'm having some issue to display that info. I made so:
in the table view controller I called the other method so:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.detailCharacterViewController = [[DetailCharacterViewController alloc] init];
[self.detailCharacterViewController setupDetailViewControllerWithName:[self.arrayCharacters[indexPath.row] objectForKey:#"name"] andSurname:[self.arrayCharacters[indexPath.row] objectForKey:#"surname"] andKind:[self.arrayCharacters[indexPath.row] objectForKey:#"kind"] andDescription:[self.arrayCharacters[indexPath.row] objectForKey:#"description"] andImage:[self.arrayCharacters[indexPath.row] objectForKey:#"URLpic"]];
}
and I implemented the method so:
- (void)setupDetailViewControllerWithName:(NSString*)name andSurname:(NSString *)surname andKind:(NSString*) kind andDescription:(NSString*)description andImage:(NSString*)url {
NSLog(#"name = %#", name);
self.labelName.text = name;
self.labelSurname.text = surname;
self.labelKind.text = kind;
self.textDescription.text = description;
NSURL *urlPic = [NSURL URLWithString:url];
NSData *dataPic = [NSData dataWithContentsOfURL:urlPic];
[self.imagePic setImage:[UIImage imageWithData:dataPic]];
}
If I look to the log i see the right things, but if I look to the GUI on iPhone or iPhone simulator it will remain blank. What's wrong with this code?
Thank you for the suggestion.
#Alex Blundell
#import <UIKit/UIKit.h>
#interface DetailCharacterViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *labelName;
#property (weak, nonatomic) IBOutlet UILabel *labelSurname;
#property (weak, nonatomic) IBOutlet UILabel *labelKind;
#property (weak, nonatomic) IBOutlet UIImageView *imagePic;
#property (weak, nonatomic) IBOutlet UITextView *textDescription;
- (void)setupDetailViewControllerWithName:(NSString*)name andSurname:(NSString *)surname andKind:(NSString*) kind andDescription:(NSString*)description andImage:(NSString*)url;
#end
The problem is that your DetailCharacterViewController's view is not loaded yet when you call setupDetailViewControllerWithName:..., so none of the outlets are connected (yet). Instantiating a view controller via alloc-init does not load its view automatically.
You could force loading the view by calling [self view] at the beginning of your setup method. This works because accessing the view property will automatically load the view if it wasn't loaded before.
Alternatively, you could make the name, surname, etc. NSString properties of the view controller and set the corresponding labels in viewDidLoad.
Are you using Interface Builder/Storyboards to design your interface? Have you made sure to connect the IBOutlets for each UITextField/View?
You should identify the objects in the header file on your view controller class, and ensure they're prefixed by IBOutlet for them to appear in Interface Builders connections pane. E.g.
IBOutlet UITextField labelName;
[...]
Then you need to go to IB and go to the connections pane for the view controller to connect each text field/view.
This problem happened to me as well.
It has to do with the fact that iOS actually hooks up your outlets really really late.
If you put a breakpoint in your setupDetailViewControllerWithName: and check your IBOutlet variables, they will be nil. That's why you are seeing a blank view. (The good old nil erroring)
The work-around I will do is to declare properties in the DetailCharacterViewController class. (Such as #property (copy, nonatomic) NSString* name).
And in the place where you are calling setupDetailViewControllerWithName:, set those properties instead such as:
self.detailCharacterViewController.name = name;
Finally in your DetailCharacterViewController's viewDidLoad:, you set the values of the labels using the saved properties. (The outlets are for sure hooked up then).
(By the way, this problem exists for prepareForSegue: as well. The destination view controller's outlets are all still nil in prepareForSegue:. So you cannot set them directly there yet)

Objective C / iPhone - how to bind UISlider to code

I'm just starting with Objective C, but I'm already running into a problem:
When I did this in the past, I had no problem with it:
I made a new UISlider, clicked it, CTRL dragged a blue line to the code, and somehow I was then able to read it's values.
However, I am now running into the problem that I can't draw those lines anymore.
By messing around I managed to get buttons to work, but the slider just won't work.
my Mainwindow.xib has got the iSaturateViewController class,
iSaturateViewController.xib has got the whole interface in it.
How can I get the UISlider to work without being able to draw that line?
How I have it now:
(in iSaturateViewController.h)
#import <UIKit/UIKit.h>
#interface iSaturateViewController : UIViewController {
UISlider * satSlider;
UILabel * satLabel;
}
#property (nonatomic, retain) IBOutlet UISlider * satSlider;
#property (nonatomic, retain) UILabel * satLabel;
-(IBAction) changeSaturation:(id) sender;
#end
(in iSaturateViewController.m):
#synthesize satLabel,satSlider;
-(IBAction) changeSaturation:(id)sender {
satLabel.text = [[NSString alloc] initWithFormat:#"Saturation: %d ", (Float32)satSlider.value];
NSLog(#"%f", satSlider.value);
}
Ath the moment the NSLog shows: 0.00000000
Also, I've set the Label in the Identity properties to: satSlider (slider) and satLabel (label).
Just to clarify my goal:
I want the slider's (satSlider) value to show in the UILabel (satLabel) via the function changeSaturation.
How can I do this?
Add the method declaration:
-(IBAction) changeSaturation:(id)sender;
to the header file iSaturateViewController.h
Then, assign the Value Changed action of your UISlider to your changeSaturation method. Right click on the slider and drag the Value Changed to the File's Owner and connect them. It should work.
You can also try this:
-(IBAction)changeSaturation:(id)sender {
UISlider *slider = (UISlider *)sender;
satLabel.text = [NSString stringWithFormat:#"Saturation: %f", slider.value];
NSLog(#"Saturation: %f", slider.value);
}
Regarding UILabel - add IBOutlet and connect it in the Interface Builder. The interface should be like that:
#import <UIKit/UIKit.h>
#interface iSaturateViewController : UIViewController {
IBOutlet UISlider * satSlider;
IBOutlet UILabel * satLabel;
}
#property (nonatomic, retain) IBOutlet UISlider * satSlider;
#property (nonatomic, retain) IBOutlet UILabel * satLabel;
-(IBAction) changeSaturation:(id) sender;
#end
Let me know if it works.
Looks like the key thing you’re missing is that the satSlider outlet isn’t hooked up, so its value is nil. You connected the slider’s action to the -changeSaturation: method, but it sounds like you didn’t also connect the satSlider property to the slider itself. Sending a message (like value in this case) to nil doesn’t do anything, and returns 0, so 0 is the value you’re getting out of it. Hook up your outlet by Ctrl-dragging from the property in the code to the slider in your UI and it should work.
You need to edit your header and add the method declaration. Then in IB, associate the first responder for value changed to the UISlider. Also slider.value, you need to read article: Setting interval value for UISlider

Issue Subclassing UIView

I've created my own custom view, with its own header and main file and corresponding nib (.xib):
The header file
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface PointsBarView : UIView
{
IBOutlet UIView *pointsBarView;
IBOutlet UIView *pointsCounterView;
IBOutlet UILabel *pointsTotalLabel;
UIImageView *handImageView;
UIImageView *powerBarOutlineImageView;
}
#property (nonatomic, retain) IBOutlet UIView *pointsCounterView;
#property (nonatomic, retain) IBOutlet UIView *pointsBarView;
#property (nonatomic, retain) IBOutlet UILabel *pointsTotalLabel;
#property (nonatomic, retain) IBOutlet UIImageView *handImageView;
#property (nonatomic, retain) IBOutlet UIImageView *powerBarOutlineImageView;
#end
I'm synthesizing everything in the main, and then in another UIViewController class I'm trying to load this view. I set up its property:
#property (nonatomic, assign) IBOutlet PointsBarView *pointsBarView;
And am adding as so:
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"PointsBarView" owner:self options:nil];
pointsBarView = [nibViews objectAtIndex: 0];
[[self view] addSubview:pointsBarView];
How do I access those subviews within my NIB? Do they need to be embedded within the pointsBarView? (pointsBarView references the main view of the NIB, and all the other views are within the pointsBarView). Should they each be a separate piece of the NIB and I need to call addSubview for each one to display?
I should note that if I do NOT connect any of the properties in PointsBarView, the view displays just fine with the code in my UIViewController class. But I want to be able to interact with each view and change properties accordingly. Any help would be great!
The general rule of thumb is: if you load it from code, you connect it up in code.
conversely:
If you instantiate in IB, you can connect outlets and actions in IB.
Here you are loading the view in code, so you have to manually connect them.
If you want to be able to connect stuff in IB add a UIVIew in IB and change the subclass to PointsBarView. IB will magically read the PointsBarView.h file and you should be able to connect outlets, targets and actions.

Sharing a UIView between UIViewControllers in a UITabBarController

I have a UIScrollView that houses a gallery of images the user can scroll through. This view needs to be visible on each of three separate UIViewControllers that are housed within a UITabBarController. Right now, I have three separate UIScrollView instances in the UITabBarController subclass, and the controller manages keeping the three synchronized (when a user scrolls the one they can see, programmatically scrolling the other two to match, etc.), which is not ideal.
I would like to know if there is a way to work with only ONE instance of the UIScrollView, but have it show up only in the UIViewController that the user is currently interacting with. This would completely eliminate all the synchronization code. Here is basically what I have now in the UITabBarController (which is where all this is currently managed):
#interface ScrollerTabBarController : UITabBarController {
FirstViewController *firstView;
SecondViewController *secondView;
ThirdViewController *thirdView;
UIScrollView *scrollerOne;
UIScrollView *scrollerTwo;
UIScrollView *scrollerThree;
}
#property (nonatomic,retain) IBOutlet FirstViewController *firstView;
#property (nonatomic,retain) IBOutlet SecondViewController *secondView;
#property (nonatomic,retain) IBOutlet ThirdViewController *thirdView;
#property (nonatomic,retain) IBOutlet UIScrollView *scrollerOne;
#property (nonatomic,retain) IBOutlet UIScrollView *scrollerTwo;
#property (nonatomic,retain) IBOutlet UIScrollView *scrollerThree;
#end
#implementation ScrollerTabBarController
- (void)layoutScroller:(UIScrollView *)scroller {}
- (void)scrollToMatch:(UIScrollView *)scroller {}
- (void)viewDidLoad {
[self layoutScroller:scrollerOne];
[self layoutScroller:scrollerTwo];
[self layoutScroller:scrollerThree];
[scrollerOne setDelegate:self];
[scrollerTwo setDelegate:self];
[scrollerThree setDelegate:self];
[firstView setGallery:scrollerOne];
[secondView setGallery:scrollerTwo];
[thirdView setGallery:scrollerThree];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self scrollToMatch:scrollView];
}
#end
The UITabBarController gets notified (as the scroll view's delegate) when the user scrolls one of the instances, and then calls methods like scrollToMatch: to sync up the other two with the user's choice.
Is there something that can be done, using a many-to-one relationship on IBOutlet or something like that, to narrow this down to one instance so I'm not having to manage three scroll views? I tried keeping a single instance and moving the pointer from one view to the next using the UITabBarControllerDelegate methods (calling setGallery:nil on the current and setGallery:scrollerOne on the next each time it changed), but the scroller never moved to the other tabs.
Thanks in advance!
Certainly you should use only one instance of your scroller view. And it will works fine without any troubles. Use method setGallery: like you did, just ensure you add your singleScrollerForAll view to view of current controller in setGallery method:
-(void)setGallery:(UIView *)aScrollerView{
[self.view addSubview:aScrollerView];
}
and call:
[firstView setGallery:singleScrollerForAll];
or
[secondView setGallery:singleScrollerForAll];
and no need to do anything in other two controllers, because when you call addSubview: the subView will be automatically removed from previous superview.