Combining UIWindows? - objective-c

I am going to add 50 windows with 4 UIButtons (with text) and 1 UILabel (with text as well) to EVERY window. Do I need to do it this way? Is there a better way?

It's never a good idea to create additional windows if you can help it, especially on iOS where there is almost never a need to do so. You should create a UIViewController subclass and make it your root view controller (this is already set up in the single view application template). Then, make a subclass of UIView — let's call it "MyQuizView." "MyQuizView" should have a custom initializer that takes five NSStrings (one for the question, four for the answers) and an integer to determine which answer is the correct one. The UIViewController subclass can then instantiate 50 of these views handing them values from the model and make them its main view's subviews.
EDIT: Here's an example of a custom initializer for a UIView subclass.
- (id) initWithFrame:(CGRect)frame question:(NSString*)ques answers:(NSArray*)ans correctAnswer:(int)correctAns{
self = [super initWithFrame: frame];
if (self) {
self.question = ques;
self.answers = ans;
self.correctAnswerNumber = correctAns;
[self setup];
}
return self;
}
A custom initializer starts with init. It sets self to the return vale of its superclasses' designated initializer, then, if self is not nil, it initializes its state–usually using the arguments passed to do so. At the end it returns self. This one assumes you have the correct properties and calls a method called setup after setting the properties to the correct values, allowing you to use them to create labels and whatnot. Alternatively you could take the values passed in and use them to immediately create the labels and buttons, set up the target actions and place them as subviews, that way you wouldn't need to keep the arguments as properties. Each button can be given a numerical tag so that you know whether or not the answer was correct or not (based on the integer passed into the initializer, which you would have to store somewhere). This is all from memory, but hopefully it's correct.

Don't waste your time creating 50 windows (if this is a MacOS question) or views (if this is an iOS question). Wow, that'd be awful.
Instead, create one single view which has four buttons and at least one label.
You can then populate the string values for each of those items from your list of questions & answers. You can keep those questions either in a plist file or a CoreData database or some parseable flat file, etc. Connect the four buttons to the (game?) controller via "IBAction" methods.

Related

Where to define NSArray and where to define Button action?

I have made an array of text strings and want to pull these out an into a label by EITHER swiping of pressing a button. So i have two different functions/methods, the button and the swipe method.
Where and how do I define the array so that these methods can refer to it? Should it be a 'extern NSArray' ?
I have uploaded the image of full code externally http://s1.postimg.org/b2e3m4v67/Sk_rmbillede_2014_05_11_kl_15_48_28.png
Not sure though if that's a violation of some rules here(?)
You want the quote to change on swipe/button press.
In your button press/swipe methods you're setting the text property of the VC's label property to something called Quoteselected. And it looks like Quoteselected is a random element of the array Quotes - or at least maybe it is, since that random number could be 6-10, and you don't have any objects in the Quotes array at those indices - so if those numbers are ever generated by the random function, your program will crash due to an index out of bounds error.
What you probably want to do is generate a new random number on each user interaction and then at that point change the value of Quoteselected to be the object at that index of the array. And then assign that to the label's text property.
As far as defining the Array - I wouldn't have done it the way you did. What you've got there is an "ivar", an instance variable. On iOS, those are typically properties. And since it's a "private" array that outside classes won't need to know about, I'd declare it as a part of the class extension.
So,
#interface BOViewController()
#property NSArray *quotes;
#end
Also note my capitalization changes, that's better style.
So now you've got an array property declared, but there's no data in it. It depends on how you created your View Controller instance. Assuming you did it in a storyboard, it would go in awakeFromNib: or viewDidLoad: (if you instantiated the VC automatically, you might put it in the initWithNibName: method).
- (void)viewDidLoad {
[super viewDidLoad];
self.quotes = #[#"Test", #"Number 3"...];
Then when you want to reference the array in other parts of the class:
self.label.text = self.quotes[0];
Note that your existing code should work, it's just not typical Cocoa coding style.

More efficient design for UITabBarController with 2 identical view controllers that differ only in data

I am designing an application with a tab bar at the bottom, representing 2 different sets of data that are the focus of my app. These sets are both of the same datatype but different collections (one is assigned entities, the other is nearby entities, but "entity" is the same type for both). Within each view controller there is also a toggle at the top for different views of that same data (one for a thumbnail-style view and another for a map view).
Based on my understanding that you wouldn't use a 2-button tab bar controller with 1 shared instance of the same view controller across both tabs, I currently have the code split into 2 separate view controllers, one for "assigned entities" and one for "nearby entities". The problem is the code here is exactly the same in each view controller. All that differs is the specific collection of entities. What I would like to do ideally is have 1 view controller with 2 arrays, 1 for each set of data, and have the data being used to populate the views change depending on which tab the user is on. Does anyone have any advice on doing this correctly? This certainly can't be that unique of an issue but I'm not finding anything online to address this exactly. Typically I would think there's something wrong with my initial design, running into a problem like this, but I think my design is sound and the approach I want to take is reasonable, but I'd like some help figuring out how to do this correctly with a tab bar controller.
Thanks.
Just have the one view controller class that knows how to handle your "entities". Create two instances of the same class, one for each tab. Assign each view controller the appropriate set of entities.
Export shared code into third helper class and use its code as a component in both viewControllers. Name this class depending on purpose of methods within it. If the methods dont use any of instance variables, you can make them all static, so you will have easy to use set of functions.
You could create a single view controller class but create a custom init method that would allow you to specify which entity type you are using when you click the tab. You could then use the entity type as a flag for your methods to look at to determine if they should treat it as type a or b.
Perhaps you could do something similar to:
EntityViewController *eVC = [[EntityViewController alloc]initWithNibName:#"EntityViewController" bundle:nil entityType:#"entityTypeA"];
[self.navigationController pushViewController:eVC animated:YES];
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil entityType:(NSString *)eType
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization for entities
// Assign your value passed in init to your ivar here -- viewDidLoad hasn't run yet.
_currentEntityType = eType;
}
return self;
}

Objective-C / Cocoa Touch: Is it good design to set label texts in the setter of the UILabel object?

Let's say I have a two subclasses of UIViewController called MasterViewController and DetailViewController.
DetailViewController has a property of type NSNumber called level and a UILabel called levelLabel.
MasterViewController has a segue to DetailViewController called ToDetail. MasterViewController's prepareForSegue is like so
- (void)prepareForSegue:(UIStoryboardSegue)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ToDetail"]) {
DetailViewController *detailVC = (DetailViewController *)segue.destinationViewController;
detailVC.level = [NSNumber numberWithInt:10]; // never mind the literal...pretend there was some algorithm for it
}
}
So then, in DetailViewController we implement the setter for levelLabel like so:
- (void)setLevelLabel:(UILabel *)levelLabel
{
if (levelLabel) {
_levelLabel = levelLabel;
_levelLabel.text = level.stringValue;
}
}
Is this good code design? Also, could you critique my code writing style? I pretty much wrote all this code on the fly so this is pretty much how I write code for the most part.
I thought of this question while showering because this is how I implement the setting of almost all the label texts that depend on a segue.
What follows is my own way of thinking about such relationships. Italics applies to your question.
You have the thing being controlled (the label) the controller (destination view controller) and the context it is being controlled within (the source view controller). This can also be expressed as model-view-controller, but I think thinking about a context can apply to much more specific and localised situations.
You should generally try to keep information flow going in one direction, from the context downwards. Objects should not have to be aware of the context in which they exist, ie they shouldn't have to ask for any information, they should be told everything they need to operate. So the source view controller should push the level to the destination view controller, the destination view controller should push this information to the label. This is what you already have, sort-of.
To build upon the above, not only should information flow in one direction, but I also try to ensure the relationships are causal, ie pushing information from one object to another should cause it to subsequently be pushed to the next object. Your code is not doing this which is probably why you have a bad feeling about it.
A more appropriate thing to do is set the text property of the label within the level setter, so that when you set or change the level, the label will update subsequently. The label may or may not be loaded so you will have to check whether it is using -isViewLoaded; -viewDidLoad is the appropriate place to set the text property upon first load.
(When I say 'push' that's just my way of thinking about setting properties or passing arguments because it implies directionality. It is really dependency injection. An example of pulling information would be delegates and data sources. But note here still the object isn't aware of any context, delegates and data sources are clearly defined as protocols, not classes, and usually within the same header file, and are themselves pushed onto the object from a surrounding context. So yes the object is asking for information, but on its own terms and from a system it has no knowledge of.)
Re coding style:
That's exactly how I write code but note Apple reserves the use of underscore prefixes

objective-c identifying contentView items

Is there a way to identify unique objects within the contentView? For example, I in my mainWin I have a NSView and 2 NSButtons. Using
[[mainWin contentView] subviews];
I can get all the objects within the mainWin. This works fine for my needs if the object is a subclass of NSView and I've given it a class, for example, in this case I've named the class vHUD and when I log the object it comes back as
<vHUD: 0x146e10>
This is fine for NSViews I am creating because for the most part if I am making them they are going to serve multiple purposes (content container, being toggled around screen, etc.) and a class should be necessary. I could subclass all the buttons (under NSObject) and go that way, but it seems like a sledgehammer approach if I end up having a lot of buttons. Is there a way I can uniquely identify all of the buttons with something descriptive in IB that can then be retrieved from the object itself? I tried "description" but that didn't return anything.
Have you tried using the tag instance variable in the NSView class?

Cocoa Touch UIViewController Properties and class design

I'm creating a custom ViewController. This VC needs to load some data that is known in the event that creates it and pushes it to the top of the NavigationController that it is going to be part of.
My question is, how should I pass data from the view that handles the custom ViewController's creation into that custom ViewController.
I've thought of four possible options, and I was hoping to get feedback on why each one is good or not for this functionality.
1) Expose public properties in the custom ViewController and set the UI elements in the view based on those properties in - (void) ViewDidLoad.
2) Expose the actual UI elements themselves and set their .text/.image/.whatever attributes as the ViewController is being created.
3) Create a custom constructor for the custom view and pass in the values I need to set up the UI elements
4) Create a custom model that both views have access to, set the data before the CustomView is created/pushed, and access that data in the ViewDidLoad event.
I'm still new to all of this, and I want to make sure that I understand the proper handling of these handoffs of data. It seems like something like this is probably a simple answer, but I'm still a little confused and its probably really important to do this right to avoid memory loss/leaks.
Also, in case anyone cares, I'm using Stanford's CS193p class on iTunes U and Mark/Lamarche's "Beginning iPhone Development" to teach myself cocoa for the iPhone. I'm working on an application with a NavigationController and a couple ViewControllers (Presence 1 if you're familiar with 193p).
Well, I believe there are advantages & disadvantages to each of those methods depending on your requirements...often it will require some combination of approaches. I believe the most common, for me anyway, is to do something like this where you give it enough to get started.
MyViewController *vc = [[MyViewController alloc] init]; // (or initWithNibName:bundle:)
// transfer vc values here
vc.value1 = aValue;
vc.value2 = anotherValue;
[self.navigationController pushViewController:vc animated:YES];
[vc release];
After your view controller is instantiated you have an opportunity to pass objects to it. Say MyViewController is a detail view then you'd give it the object it will be displaying the details for. Or, if it's a table view you can give it the NSArray it will need for display. Then in viewDidLoad or awakeFromNib or awakeFromCoder, or... you can fill out the view...so to speak.
#1 is fine, with or without #3 (these two are not mutually exclusive)
#4 is my preferred solution. For instance, if I had a UserViewController, I would probably also like to have a User object and create it this way:
User *user = [self.users objectAtIndex:someIndex];
UserViewController *uvc = [[[UserViewController alloc] initWithUser:user] autorelease];
#2 is not a good idea. Objects should not access the UI elements of other objects. Much trouble comes from this when you decide to change your UI around (and you will).