How can I control the number of multiple UITableView's at build-time, using a single #define number?
My app presently needs 4 UITableView's. Later, the number will increase, so I want to control this at build-time with a single #define of how many.
But I get an error when I use an array in the #property declaration:
#define TOTAL_TX_CHANNELS 4
#interface blah() <UITableViewDataSource, UITableViewDelegate CBPeripheralDelegate>
{
}
#property (strong, nonatomic) UITableView* channel_tableView[ TOTAL_TX_CHANNELS ] ;
What's the trick? Should I use an NSArray or something?
Yes, use NSArray.
If it's configured in Interface Builder you could use:
#property (weak, nonatomic) IBOutletCollection (UITableView) NSArray * tableViews;
Why exactly do you need to control the # of UITableViews?
If you must do this, you could do some mathematical operation.
int adder = 4;
int currentNumberOfTvs = 4;
//Do some logic, that will add the number of current table views
if (...) //Test if user has added, or however, if another table view was added,
{
adder++;//now adder = 5. So you have a current count of how many table views there are
}
Hope that helps:)
I agree with the others that this is a terrible implementation. Wain's answer is very good.
However, still it is possible to implement it this way, just make sure to declare the property as
#property (assign, nonatomic) UITableView** channel_tableView;
Reference counting works only on Obj-C objects but you are creating a C array here.
Note that you have to use malloc to allocate the storage.
If you don't want to malloc, implement it as an ivar:
#interface Blah {
UITableView* channel_tableView[4];
}
#end
The solution was not to use an a property array, but to use an UITextView array at implementation scope, the .m...
UITextView* channel_pipe_textview[ TOTAL_TX_CHANNELS ];
Then, in function viewDidLayoutSubviews I created a UITableView for each channel, and added the standard functions for initializing the cells, all by following Apple's excellent UITableView programming guide.
It worked perfectly, completely independent of storyboard: I get four vertical side-by-side columns displaying any kind of images and text details regarding every device on the channel, with the ability to scroll, which allows any number of devices per channel.
The Apple UITableView Programming Guide also has tutorial and sample code for programmatically generating UITableViews solely at run-time.
Related
There are at least 3 methods of creating an IBOutlet in Objective-C, for making iOS 10 App, in Xcode 8.
Method 1: in ViewController.h
#interface ViewController : UIViewController
#property (nonatomic, strong) UILabel *textLabel;
#end
Method 2: in the interface of ViewController.m
#interface ViewController () {
IBOutlet UILabel *textLabel;
}
#end
Method 3: in the interface of ViewController.m, using #property
#interface ViewController ()
#property (nonatomic, strong) UILabel *textLabel;
#end
Given that the textLabel has to be accessed & its text is needed to be updated frequently, which method is the correct way to do so?
That all depends on whether you need your outlet to be accessible to classes outside of the containing one; generally I would discourage this because it is good practice to keep your view controllers responsible for updating your UI and not pass this task around to other classes. With this being said, Method 3 would be the best option, however, if you do have to access your object from another class, then simply use Method 1 so it is exposed in your class header.
Method 2 utilises iVars rather than object properties and is not the proper way to declare outlets, it may even cause unexpected behaviour so it is best to avoid this method.
Your code contains no proper IBOutlet. Outlets are connections to Storyboard.
Method 1
This is a property. As it is in .h file, it can be reached from outside. The Objective-C pattern for public.
Method 2
This is an iVar. Do not use iVars if you do not have to.
Method 3
This is a property. As it is in .m file, it can not be reached from outside. The Objective-C pattern for private.
Method 4
A proper IBOutlet looks like this:
#interface ViewController ()
#property (nonatomic, weak) IBOutlet UILabel *label;
#end
It is a simple property. You have to decide if you put it in .h or .m file depending on whether or not you want to publish it.
The IBOutlet simply makes the property connect-able to Storyboard. It's an annotation for Xcode and does not alter the semantic of your code.
Edit 1:
As Sulthan correctly mentions in the comments:
In most situations the correct design pattern is to hide outlets because it's an implementation detail. External classes should not set data directly using views.
Edit 2:
Why "not to use iVars if you do not have to" (2)
Opinion based:
I consider it as good OOP practice to use getters & setters (and thus not to access the variables directly). Also code is easier to read as you know while reading what x = self.variable (property) and x = variable (local variable) are.
If you have to use iVars for some reason, it is common to (and I would recommend to) prefix the name with _. x = _variable (iVar).
Now I understand that this question has been asked before, but the answers were unsatisfactory. My issue is that I have a view controller with a view and stuff in it including a label. I added a bunch of code for it and now I'm expanding on it. I now have an issue where I've decided to add another UIView to my interface and it has a label and that label is going to function EXACTLY like a label I have in my first UIView. My problem is that I don't want to have to go in my view controller method and add another line of code each time I manipulate that first label. Is there anyway I can link another label to my initial IBOutlet I have set for my first label? Or do I have to go in my code and add an extra line of code everytime I manipulate that first label?
It depends on what you want to do to that label. If you're looking to change some of the attributes of the label in the same way (e.g., font, text colour, alignment) then you can put both labels in an IBOutletCollection and iterate over the collection in your view controller.
If you want to have different data in the label, but other attributes the same, then you'll need a separate IBOutlet for that label.
You can combine the two techniques as well. e.g.
(interface)
#property (weak, nonatomic) IBOutlet UILabel *firstName;
#property (weak, nonatomic) IBOutlet UILabel *lastName;
#property (strong, nonatomic) IBOutletCollection(UILabel) NSArray *labels;
(implementation)
- (void)viewDidLoad {
[super viewDidLoad];
for (UILabel *aLabel in self.labels) {
// Set all label in the outlet collection to have center aligned text.
[aLabel setTextAlignment = NSTextAlignmentCenter;
}
[self.firstName setText:#"First Name"];
[self.lastName setText:#"Last Name"];
}
Basically the simple answer is no. Whether you use outlets or an outlet collection or tags or whatever, you ultimately have one reference in your code to one label in your interface, and another reference in your code to another reference in your interface. You can compress your mode of expression so as to cycle readily through those references (as suggested in a different answer), but the basic fact is inescapable that, ultimately, the only way to "talk to" a label is through the one reference that points to that label and to that label alone.
The only way of getting around that is not to use direct references at all. For example, a single message can be sent to multiple recipients by using an NSNotification. So you could have two instances of some UILabel subclass of your own, and "shout" to both instances simultaneously by posting a notification from your view controller - the notification is then automatically passed on to both labels, because you have arranged beforehand for them to register for it.
Similarly, another alternative is that you could use key-value observing so that a change in your view controller is automatically propagated to both labels automatically because they "observe" the change, meaning they are sent notifications - really just an inverted form of NSNotification. (If this were Mac OS X, you could make a simpler, safer version of this arrangement by using "bindings".)
However, I really cannot actually recommend that approach. The truth is that we still live in an excruciatingly primitive world of text-based programming, one line at a time, one command at a time, one reference at a time, and we must just bite the bullet and get on with it.
Swift 3, Xcode 8
Create a prototype cell with objects
then add another prototype
It will copy the objects from the first prototype cell.
The new objects will be connected to the same IBOutlet
Also, copy and pasting objects maintains IBActions, but does not maintain IBOutlets.
I hope this answers your question, as none of the other answers had this work around.
I started Objective-C programming a couple weeks ago, so my understanding of how all these pieces fit together & the order they're all happening is still confusing to me. I'm trying to make a JSON API call to one of my apps using NSURLSession. That all works flawlessly, but I want to update a label with a piece of the data that's returned & anytime I look at/try to update the label, I get null.
Some of the SO posts I've found that are similar to my problem include: this, this, this, and this. Coming from the world of Ruby on Rails, I haven't had to deal with async concepts at all, but I know I'm close.
Here's the relevant snippet of code in question:
if (!jsonError) {
NSDictionary *skillBuildData = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"%#:", skillBuildNameLabel.text); // should say "Build Name" but returns null
NSLog(#"%#", skillBuildData[#"name"]); // correctly prints the result
NSLog(#"%#:", skillBuildNameLabel.text); // should have contents of skillBuildData[#"name"] but returns null
skillBuildNameLabel.text = skillBuildData[#"name"]; // obviously results in null, but I don't know why.
});
}
EDIT:
Not sure if it's relevant, but here's the bulk of my ViewController.h to give you an idea of the outlets & actions in this very simple app. One button, one method, the IBOutlet that links the button & JSON call method, and a label:
#interface ViewController : UIViewController {
IBOutlet UILabel *skillBuildNameLabel;
IBOutlet UIButton *getSkillBuildDataButton;
}
- (void)getSkillBuildDataById:(int) skillBuildId;
- (IBAction)buttonPressed;
It seems like I'm very close, I just can't see the link I'm missing. Thank you so much in advance!
EDIT 2:
Check out Ben Kreeger's comment to the response I marked as the answer. I didn't connect the actual label in my storyboard to the outlet I created in my ViewController.h. I had no idea you could drag the line from the element in a storyboard to an actual line of code. That was the missing piece. Looks like I have a lot more to learn about Xcode & Objective-C. Thanks to all who helped me out!
You may have more luck declaring your IBOutlets as properties (#property) instead of as instance variables (see this answer for why weak instead of strong).
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *skillBuildNameLabel;
#property (weak, nonatomic) IBOutlet UIButton *getSkillBuildDataButton;
...
#end
Then you'll be able to reference them as self.skillBuildNameLabel and self.getSkillBuildDataButton in your implementation.
Beware that this self.x notation inside of a callback like that may lead to what's called a retain cycle. If this is the case, Xcode will warn you about this. Here's a bit on retain cycles for you.
Footnote: I rarely ever see (and never write) this syntax anymore for declaring instance variables.
#interface ViewController : UIViewController {
IBOutlet UILabel *skillBuildNameLabel;
IBOutlet UIButton *getSkillBuildDataButton;
}
Use properties instead.
You are doing the logging before setting the text. move
skillBuildNameLabel.text = skillBuildData[#"name"];
to the top of the async block, above the NSLog statements.
My problem is a follows
I have a UIViewController subclass which holds a UISegmentedController and four tableviews that I layed out in interface builder.
#interface MultiTableHoldingView : UIViewController{
}
#property (strong, nonatomic) IBOutlet DataTV *dsDataTV;
#property (strong, nonatomic) IBOutlet EnviroTV *dsEnvironmentTV;
#property (strong, nonatomic) IBOutlet LocationTV *dsLocationTV;
#property (strong, nonatomic) IBOutlet Note_AnimalTV *dsNoteAnimal;
#property (strong, nonatomic) IBOutlet UISegmentedControl *diveElementSegmentController;
#property (strong, nonatomic) DiveSite* currentSite;
- (IBAction)diveElementSegmentControllerDidChange:(UISegmentedControl *)sender;
-(void) setFreshWaterColor;
-(void) setSaltwaterColor;
#end
setFreshWaterColor and setSaltWaterColour just set the background colour properties of the MultiTableHoldingView instances UIView and the four tableviews it contains. Both these method work fine when called from MultiTableHoldingView's viewDidLoad method. Heres one of them
-(void) setSaltwaterColor{
DLog(#"in set salt water colour");
self.view.backgroundColor= SaltWaterColor;
_dsLocationTV.backgroundColor=SaltWaterColor;
_dsDataTV.backgroundColor=SaltWaterColor;
_dsEnvironmentTV.backgroundColor=SaltWaterColor;
_dsNoteAnimal.backgroundColor=SaltWaterColor;
}
The other is the same except sets to FreshWaterColor - both are #define i have set up.
I use the segmentedController to turn the hidden properties of the various tableviews on and off. All nice and simple. The tableviews are pulling in their data. Working fine.
When selecting one of my tableview cells on one of the tableViews I want to change the background colour of both my tableview ( in fact all of my tableviews ) and the UIView that is the superview
self.superview.backgroundColor = FreshWaterColor;
works fine for reaching back and changing the instance of MultiTableHoldingView views background property but I want to call the instance of MultiTableHoldingView's setFreshWaterColor and setSaltwaterColor methods.
I have imported MultiTableHoldingViews header into the relevant tableview (EnviroTV), so it knows about it its superviews methods. But if I try to call either of the two methods on self.superview the methods do not show up and if i type them in full I get an the following error
no visible interface for 'UIView' shows the selector 'setFreshWaterColor'
So i checked what kind of object the superview was and its a "class of superview UIViewControllerWrapperView"
I search on this and its apparently "
This is a private view used by the framework. You're not supposed to modify it or whatsoever."
I'm obviously missing something here - how should i call the method on the instance of MultiTableHoldingView ?
Thanks in advance
Simon
Doh - its just delegation as danypata mentions in the comments - i've posted exactly how I did this as an answer below. Tried to make it as clear as possible how delegation works
THE SOLUTION
Step one - get more sleep before coding .
This really is basic objective-c stuff - I just went off at a tangent, looking for someway else to do it, getting confused by my discovery of UIViewControllerWrapperView along the way.
The solution, as danypata rightly suggests in the comments, is to use delegate -a common design pattern in Objective-C - just like you do, for example, when you use another class to supply tableview data
As I've been a numpty and wasted hours of my time today I'll try and make the 'how' clear for other relative newbies or people having an off day and not thinking straight.
In my case I set this up as follows
In my subview class's interface file - EnviroTV.h - I define the following protocol just before the #interface declaration
#protocol EnviroTVProtocol <NSObject>
-(void) setFreshWaterColor;
-(void) setSaltwaterColor;
#end
Then in the #interface section of the same file I add a property of type id which must conform the protocol I just declared .
#property (nonatomic, strong ) id<EnviroTVProtocol> colorChangeDelegate;
You make the type an id - a generic object - as you really don't care what kind of object is going to act as your delegate just that it implement the methods that you need it to run. When an object declares itself to implement a protocol its just promising to implement the method(s) that are required by the protocol
So, when I want to run the methods on the superviews class I call
[self.colorChangeDelegate setFreshWaterColor];
Or
[self.colorChangeDelegate setSaltWaterColor];
The final piece of the delegation pattern is to go into the class thats going to be the delegate (in this case my MultiTableHoldingView class ) and state that it conforms to the protocol
I do this in the MultiTableHoldingView.h file
Changing this line :
#interface MultiTableHoldingView : UIViewController
into this line :
#interface MultiTableHoldingView : UIViewController <EnviroTVProtocol>
means this class promises to implement all the required methods of the EnviroTVProtocol.
Luckily I had already written the two methods. So when I compiled it ran correctly
Newbies - don't be afraid of delegation - its awesome and not as complex as you first imagine it to be
Meanwhile, if anyone can explain what UIViewControllerWrapperView is .....
I'm basically an Android programmer and on my way to learning some skills on iOS platform.
I have an Android application, which has a custom UI component which looks like
.
I want to create a similar re-usable UI component for my sample iOS application. I'm not able to get any lead on how to do this in iOS.
On Android I can create a composite UI element using a layout file but in iOS i'm not sure if i can create a composite UI element or extend UIView and then somehow layout Text and image components inside it.
I'm looking for some leads on implementing this. I plan to have a multiple instances of these component on screen & the values gets updated from a web service.
I recommend something called interface builder in iOS.
It is a place where you can visually place elements that the user interacts with and you can see how the design looks as you layout your structure.
For tutorials, you can look at http://mobile.tutsplus.com/tutorials/iphone/interface-builder/
or search up "ios xib tutorial"
Hope this helped!
If you want it to be a simple view, then you could create a UIView sub-class with a few UITextFields's and probably an UIImageView or two that all have outlets so that your controller can make changes to it. For instance:
#interface StockInfo <UIView>
#property (nonatomic, strong, readonly) UITextField *ticker;
// You may want to make these numbers so that you can do calculations with them, and then update the text field automatically
#property (nonatomic, strong, readonly) UITextField *price;
#property (nonatomic, strong, readonly) UITextField *priceChange;
// This could be automatically calculated based on the price and priceChange if appropriate
// It could also automatically show the Up or Down indicator
#property (nonatomic, strong, readonly) UITextField *percentChange;
#end
Then, your controller could create an instance and set the various properties:
StockInfo *djia = [[StockInfo alloc] init];
djia.ticker = #"DJIA";
djia.price = #"14550.35" ;
djia.priceChange = #"-111.66";
// ...
You can create the actual UI elements within the view either in Interface Builder, or do it in code. Which to do is kind of a personal preference. There are plusses and minuses to both, and building the view in code in this case would be pretty easy and not require you to have two files in order to use the control.