Re-using IBOutlet programmatically - objective-c

I have an UIView created in IB with some labels and custom colours and linked that as an IBOutlet so I could have access to it in my viewController.
#property (strong, nonatomic) IBOutlet UIView *tile;
although I would like to create more of this outlet within a loop:
- (void)viewDidLoad {
[super viewDidLoad];
int i = 0;
while (i <= 9) {
UIView *cTile = [self.tile copy];
[self.view addSubview:cTile];
i += 1;
}
}
So I'm trying to copy this tile outlet and then add the copied view to the main view.
Apparently this can't be done and returns an error.
Is it possible to achieve such behaviour and re-use/duplicate an IBOutlet?

I dont think what you want to do is possible as UIView doesn't conform to the NSCopying protocol.
Best bet would be to create your "tile" as a UIView subclass or crate a nib for it and then you can reload it as many times as you want.

Related

UITableViewCell - registerNib subclass not finding nib as subview

In the viewDidLoad in my UITableView I am calling:
[self registerNib:[UINib nibWithNibName:#"MyCell" bundle:nil] forCellReuseIdentifier:#"MyCellRI"];
Then in the cellForRowAtIndexPath I have the following:
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyCellRI"];
[cell setup:[items objectAtIndex:indexPath.row]];
Inside the setup method I have a look on [self subviews] to go through the subviews and assign content based on key/value but in this loop I find UITableViewCellContentView's but I do not see the my custom view defined in the NIB.
In the NIB I have assigned Files Owner & The custom class of my cell. The cell is showing I just can't find the NIB view in the subviews.
Please advise.
James
Iterating through subviews is hard work) Try to avoid it. it is not very good practice because it is not reliable. For instance if you decided to change your custom cell UI you will have to change method where you're iterating through subviews.
Try to do following:
Let's say that there is custom cell with UIImage and 3 strings (e.g. person's name, age and cell phone number)
Implement 3 functions in your custom cell to set this values as follows:
MyCustomCell.h
... .h file standart code and then at the end ...
#propert(nonatomic, strong) IBOutlet UIImageView *myImage;
#propert(nonatomic, strong) IBOutlet UILabel *myFirstLabel;
#propert(nonatomic, strong) IBOutlet UILabel *mySecondLabel;
#propert(nonatomic, strong) IBOutlet UILabel *myThirdLabel;
- (void)setUIimage:(UIImage *)_image;
- (void)setFirstString:(NSString *)_string;
- (void)setSecondString:(NSString *)_string;
- (void)setThirdString:(NSString *)_string;
MyCustomCell.m:
#synthesize myImageView, myFirstLabel, mySecondLabel, myThirdLabel;
- (void)setUIimage:(UIImage *)_image {
self.myImageView.image = _image;
}
- (void)setFirstString:(NSString *)_string {
self.myFirstLabel.text = _string;
}
- (void)setSecondString:(NSString *)_string {
self.mySecondLabel.text = _string;
}
- (void)setThirdString:(NSString *)_string {
self.myThirdLabel.text = _string;
}
and finaly in your tableView controller cellForRowAtIndexPath
[myCustomCell setUiImage:someImage];
[myCustomCell setFirstString:oneString];
[myCustomCell setSecondString:twoString];
[myCustomCell setThirdString:threeString];
Hope this approach will help you. Feel free to ask if you have some questions

Unable to implement simple view scrolling in iOS

I'm having a hard time implementing a simple scroll on my detail view.
The app is straightforward with a Master and Detail views.
When the user taps an item on Master, the Detail view is pushed with larger photo, blog text and etc.
I would like the entire Detail view to scroll, so if the picture is tall, or the text is long, they can scroll vertically to see/read more. I do not want the user to scroll these items individually. It should feel like webpage scrolling.
Currently my Detail view loads OK but I can't make it scroll.
My DetailViewController.h
#import <UIKit/UIKit.h>
#interface DetailViewController : UIViewController {
IBOutlet UILabel *postTextLabel; // wired to Text Label
IBOutlet UILabel *postAuthorNameLabel; // wired to Author Label
}
#property (strong, nonatomic) id detailItem;
#end
My DetailViewController.m
#import "DetailViewController.h"
#interface DetailViewController ()
- (void)configureView;
#end
#implementation DetailViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self configureView];
}
- (void)configureView
{
if (self.detailItem) {
NSDictionary *post = self.detailItem;
NSString *postText = [post objectForKey:#"post_text"];
NSString *postAuthorName = [post objectForKey:#"post_author_name"];
postTextLabel.text = postText;
postAuthorNameLabel.text = postAuthorName;
}
}
#end
Structure on IB:
Any ideas on what's missing to make this work?
I would do the following:
1)(optional)turn your View into a scrollview by dragging it into the view in the scruture list on the side.
2)link the scrollView into your viewcontroller .h and make an Outlet connection something like this
#property (strong, nonatomic) IBOutlet UIScrollView *scroller;
(make sure you add #synthesize in the .m if you add this manually)
and make sure it is connected in IB!
3)set the contentsize of the scrollview in viewDidLoad method
scroller.contentSize = CGSizeMake(320,550);
NOTE: IBOutlet UILabel *postTextLabel; should actually probably be a UITextView so you can access ContentSize
Which would allow for the following.
CGRect frame = postTextLabel.frame;
frame.size = postTextLabel.contentSize;
postTextLabel.frame = frame;
scroller.contentSize = CGSizeMake(320, frame.size.height+200);//200 or how ever much space is above the textView
and again that only works if you use a UITextView
for ipad replace 320 with 768 or 1024, or whichever depending on orientation
The best way to connect it in IB is like this holding down control and dragging to the .h file
and make sure it is set to automatic like in the picture, and pointing to> the .h of your view.
Adding it like this also automatically adds the #synthesize for you.
Make sure UserInteractionsEnabled are checked here for our scrollview

Maintain a Link to Subviews in View Controller

Here's my situation: I have a UIViewController that manages a hierarchy of subviews, perhaps as shown below:
This view is built from a .xib. I would like to be able to maintain access to each subview of topView – that is, I want a pointer to each so that I can, for example, say something like:
[button1 setText:#"Hello!"];
Usually, to do this, I wire up each element to which I would like access using Interface Builder, resulting in a header that looks something like this:
#interface MyViewController : UIViewController
{
__weak IBOutlet UIView *view;
__weak IBOutlet UILabel *label;
__weak IBOutlet UIButton *button1;
__weak IBOutlet UIButton *button2;
}
#end
These instance variables are __weak, which is fine, since by the time my view controller "gets" them, they're already owned by my view controller's root view (which, confusingly, I labeled "topView" in my quick diagram). In fact, I want these references to be weak – when my root view is released, so too should all its subviews be released. Great.
But let's say I want to create a new element of the UI, maybe a custom button, entirely in code. I'll call this element CustomViewClass, which will be subclassed from UIView. The instance of CustomViewClass that I will create will be called customButton. As with the other subviews of my view, I would like "access" to customButton so that I can interact with it. I know, however, that like any other subview, customButton will be owned by its superview, and that's how it should be – again, I want it to be released whenever my view is released. This makes me think that I should declare this view as a __weak instance variable or property of my view controller. Let's do that:
#interface MyViewController : UIViewController
{
__weak IBOutlet UIView *view;
__weak IBOutlet UILabel *label;
__weak IBOutlet UIButton *button1;
__weak IBOutlet UIButton *button2;
__weak CustomViewClass *customButton;
}
#end
Then, in my implementation:
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
customButton = [[CustomViewClass alloc] init];
[[self view] addSubview:customButton];
}
#end
As you've probably already realized, this won't work, and the compiler will throw a warning to boot. Something like:
Assigning retained object to weak variable; object
will be released after assignment
I currently dodge this sort of warning with some very poor style:
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
CustomViewClass *customButtonLocal = [[CustomViewClass alloc] init];
[[self view] addSubview:customButtonLocal];
customButton = customButtonLocal;
}
#end
That way, I get what I want:
An instance of CustomViewClass on the screen...
...with exactly one owner, its superview...
...and no lingering variables (customButtonLocal is released immediately after the block ends).
But this can't be the "right" way to do this. So, finally, my question:
How should I be allocating and instantiating this programmatically-created __weak variable without using this middle-man workaround?
Make CustomViewClass *customButton a strong reference.
The reason why you usually declare variables for your subviews as __weak IBOutlet is that the existence of these links does not imply ownership. Subviews are owned by the object instantiated from a NIB/Storyboard. You own that object directly, and you also own its dependent objects indirectly.
The customButton is a different story: you create it programmatically, so your NIB/Storyboard does not own it. Therefore, you should make the reference to it __strong (which is the default when there are no ARC modifiers).

How do I access or manipulate variables of one class from another in Xcode

I am a novice to IOS programming hence struggling a bit with the below problem. I will do my best to describe the problem and any help is greatly appreciated.
I have the following created:
AboutViewController (.h, .m and .xib ) which has two subviews called - mainView, infoView. mainView and infoView interfaces are created in the XIB file.
TestView ( View to deal with initiating, toggling between mainView and infoView )
TestView.h is as follows:
#interface TestView : UIView {
IBOutlet UIView *mainView;
IBOutlet UIView *infoView;
UILabel *lbltitle;
UIImageView *imgIcon;
IBOutlet UITextView *txtInfo1;
IBOutlet UITextView *txtInfo2;
IBOutlet UITextView *txtInfo3;
}
#property (nonatomic, retain) IBOutlet UILabel *lbltitle;
#property (nonatomic, retain) IBOutlet UIImageView *imgIcon;
TestView.m is as follows:
#import "TestView.h"
#import <QuartzCore/QuartzCore.h>
#implementation TestView
#synthesize lbltitle, imgIcon;
-(void)awakeFromNib
{
[self addSubview:mainView];
}
In portrait mode the views are working great but when it comes to landscape mode, the views are all kinda screwed up. I tried to use the XIB but I guess you can only do so much so I decided to do this programmatically.
In the AboutViewController.m, I am trying to override willAnimateRotationToInterfaceOrientation method and place the objects based on the orientation. When I put breakpoints, I can see that code is being called except it is not translating into the UI i.e., no change in the UI. What am I doing wrong ? Should I be approaching this in a different way. Any suggestions or guidance is greatly appreciated.
- (void)willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
TestView *t = [[TestView alloc]init];
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
[t.imgIcon setFrame:CGRectMake(10,74,165,190)];
[t.lbltitle setFrame:CGRectMake(20, 10, 387, 21)];
}
else
{
[t.imgIcon setFrame:CGRectMake(10,74,165,190)];
[t.lbltitle setFrame:CGRectMake(189, 10, 387, 21)];
}
}
Well the particular problem is you manipulate a view that you just created using
TestView *t = [[TestView alloc]init];
You don't display the view anywhere, so it sits in memory. You can apply all changes you want, but to be able to see them, you must first display the view:
Find the appropriate parent and do:
[parentview addSubview:t];
In more general terms you shouldn't be creating new views in the rotation handling code.

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