I'm having trouble understanding the idea of objects. From what I've read, they're instances of a class. When learning swift, they're quite easy to understand. Simply create a class and create an instance of it, and from there, you can modify it's properties and call its methods:
class ExampleClass {
let ExampleProperty = "rabbit"
}
let exampleInstance = ExampleClass()
But I don't see how that translates when using iOS, since I haven't seen any objects being created explicitly yet:
var example = Wss()
So my questions are:
Are things like buttons, labels, and sliders objects?
-If so, where's the "code" behind them? Why do buttons, labels, etc. display even before they're connected through outlets and actions to the View Controller? Is there a hidden "var thisButton = ThisViewController()" embedded into each of those sliders and buttons?
If my assumptions are wrong, can someone explain to me how objects work?
"Is there a hidden "var thisButton = ThisViewController()" embedded into each of those sliders and buttons?"
No, and this is exactly where interface builder excels. Much of Xcode's modern Interface Builder comes from NeXTSTEP. When you drag out a new UI component like NSButton and place it on your story board, Xcode is instantiating a new object of the NSButton class for you. When you save your file, Xcode serializes all the objects of your story board into a .nib file. At the time when this was invented, it was quite revolutionary, all made possible because of the dynamism of Objective C. It made GUI programming much simpler and dynamic. Every object in your story board is aware of its class. For example, when you instantiate a new NSButton, you can open the inspector and see for yourself that its class is NSButton. When you add custom views to your application, they keep track of their class in the same way. Whenever a nib file is loaded, these views are instantiated from their classes. You might have noticed that you never override the initializer of your views. Instead, you override methods like awakeFromNib. This is because there's a lot of behind the scenes work being done for you, from the time the object is first instantiated, to the time. During this time IBOutlets and IBActions are bound for you.
Competitors tried to make similar interface building applications, but they ultimately resorted to doing code generation behind the scenes. In these systems, when you saved your interface file, the program would generate a source file that contains code that instructs how to instantiate these objects anew whenever the interface is loaded. However, it proved significantly more complex a task then just serializing the objects, so these systems were error prone, and significantly harder to debug (because you'd be trying to debug machine generated source files).
Answering your questions:
Yes. Your objects are just being created from a NIB, or Storyboard. So the NIB, or Storyboard, will create those visual (UI) elements for you, which you can then be accessed via the IBOutlets
Your assumptions, are not completely wrong as in, there is in fact something allocating those objects for you. The NIB, or Storyboard, just describe a way for those objects to be created. Also some other customisations, like frames, colors, etc.
More about how this ties up can be found here.
Building on Alexander's answer:
UIView objects have a method init(frame:) that lets you create a new UIView object with a specified frame.
Other UIView subclasses might have init methods that take additional parameters.
UIView objects also support an init method init(coder:) that knows how to create an object from a stream of stored data. This is known as "deserializing" the object, or converting it from a byte-stream back into a running object.
When you build an object in a Storyboard or XIB file in Interface Builder, the system serializes the object into a byte stream and saves it into your Storyboard/XIB.
Then when you invoke the storyboard scene/XIB, the system reads the data stream and uses it to recreate (deserialize) the objects that are described in the storyboard/XIB.
The effect is essentially the same as if you wrote a bunch of code that created and configured all your views, but instead of writing all that code you are able to build your interface in Interface Builder, which is faster and easier to create, and MUCH faster and easier to update and maintain than a bunch of custom code.
But I don't see how that translates when using iOS, since I haven't seen any objects being created explicitly yet
There's no difference between the objects in iOS and what you understand objects to be. Objects are instances of a class. What you need to understand is that your own code is not the only place where objects can be created, and your own code will often interact with objects created outside your code. Here's a simple example:
let defaults = NSUserDefaults.standardUserDefaults()
Here defaults gets a reference to a user defaults object that the system provides. You never need to instantiate NSUserDefaults yourself.
Are things like buttons, labels, and sliders objects?
Yes, those are instances of UIButton, UILabel, and UISlider, respectively.
If so, where's the "code" behind them?
It's in the UIKit framework. You don't get to see the source code for those classes, but you can still use them by linking the framework into your app.
Why do buttons, labels, etc. display even before they're connected through outlets and actions to the View Controller?
You're talking about storyboards here. When you set up a view in Xcode's storyboard editor, the data that's stored in the storyboard file is essentially an archive containing serialized objects. When a view controller is instantiated from a storyboard, the objects in the storyboard are recreated from that data and then connected to the view controller's outlets. You can start this process yourself by instantiating a new view controller like this:
let storyboard = UIStoryboard(name: "MyStoryboard", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "MyViewController")
You don't usually need to do that, though, because the segues in your storyboard provide for transitioning between scenes, including creating the view controller that's the destination of a given segue.
Related
I just started learning XCode, objective-c, iOS, and all that. This is my first foray into app development. I'm not new to development, just iOS development and XCode.
So I'm going through a Udemy course that has me working with storyboards and I have some concern because every professional iOS developer I know uses something called Interface Builder which apparently removes the need for storyboards.
I've only just started, so I still have only a rough idea of what a storyboard even is....it seems to just be a graphical representation of a single page view. I don't know how it relates to this so-called Interface Builder and what their relationship is.
By going through this course learning with storyboards, am I being put on the wrong track? Or is this a useful beginning step before transitioning to the Interface Builder? Will using storyboards help me to work with that later? Am I wasting my time?
The Interface Builder refers to the part of Xcode that lets you view and edit Storyboards and .xib files (it automatically opens when you click on such a file).
A .xib (or 'nib') file is a representation of a single logical view in you application (on iOS, typically a UIViewController with a number of views, such as a UIScrollView and a UINavigationBar).
A storyboard is a collection of such views, and can be used to build transitions from views to other views, among other things.
I recommend reading Apple's Documentation on storyboards to get an idea of what they can do for you.
...
Interface Builder which apparently removes the need for storyboards.
...
Actually, storyboard is a concept within Interface Builder.
It's a visual representation of the entire app flow.
I think all you need is a quick-read through the Apple Interface Builder Doc.
In basic understanding, IB is a drag-drop area to visually create your views.
To quote:
You create your app’s user interface in Interface Builder. Select a
user interface file in the project navigator, and the file’s contents
open in Interface Builder in the editor area of the workspace window.
A user interface file has the filename extension .storyboard or .xib.
Logical Example: Instead of programmatically coding a UIButton and setting it's frame or constraints, you go to the Interface Builder, select a UIButton object and place it where you would want it to go. You will also specify what the object name and what method it responds to. (but this will need the object name and method name to be defined in the respective class's .m or .h file that the view is associated with)
Interface Builder can be either XIB/nib or Storyboard. Latter of which is the more recent (and recommended) method provided by Apple.
Using a storyboard, you have one single file, a .storyboard file that will represent the entire app flow.
An app can have multiple screens/views and so a storyboard will basically represent multiple UIViewControllers, each of which will be tied to a particular class.
For example, in this storyboard, you can visually see (assumptions from here on) that you have, say, 5 screens in the entire app:
Screen 1 begins with maybe a UINavigationController
Screen 2 is the root view of this UINavigationController, say, LoginVC (tied to LoginVC.m and LoginVC.h).
A button on LoginVC takes you to, say, SignUpVC (tied to SignUpVC.m and SignUpVC.h)
Another button on LoginVC takes you to, say, ProfileVC (tied to ProfileVC.m and ProfileVC.h)
Screen 3 is SignUpVC
A button takes you back to LoginVC
Screen 4 is ProfileVC
A button takes you to SettingsVC (tied to SettingsVC.m and SettingsVC.h)
A button logs you out and takes you back to LoginVC
Screen 5 is SettingsVC
Q.
By going through this course learning with storyboards, am I being put on the wrong track?
A.
No, absolutely not. You're going in the right direction.
However, i think knowing the former XIB/nib method is worth your time as well.
Plus, programmatically creating UIViews is highly recommended.
Q.
...is this a useful beginning step before transitioning to the Interface Builder?
A.
It's already getting you acquainted with the Interface Builder so there won't really be much "transitioning" required.
Q.
Will using storyboards help me to work with that later? Am I wasting my time?
A.
Yes, unless you work in a team under version control, in which case, XIB still looks good.
So what's XIB?
It's still within the Interface Builder scope...
Break a storyboard into it's individual views and you have multiple files (.xib files) that represent a UIView or UIViewController for a single class. (hence helps when you work in a team under version control)
So now... instead of having one .storyboard, you will have multiple .xib files that will be associated to all those classes that (you deem) needed a visual representation.
Links:
Storyboard
XIB
Storyboard :
Has a nice UI designer , WYSIWYG , drag, resize design editor, that generates code and sync with manual code changes.
SwiftUI:
Code is the single source of truth. No Designer. Lots of hard coding
I'm learning iOS development stuff and what I have found in tutorials and books is that controller layer usually has access to the View's controls directly (textfields, labels etc.). Let's consider such an example:
Assume, that View has a label called lblResult and a textfield called txtDataToAnalyze. Than in controler interface we've got something like this:
#property (nonatomic, retain) IBOutlet UILabel* lblResult;
#property (nonatomic, retain) IBOutlet UITextField* txtDataToAnalyze;
and some #synthesize statements in the implementation file.
I have some experience with JavaSwing development, where most of thinks I'm writing manually without any GUI Builders, and what I usually do in MVC is to access the View's controls via getters/setter. For example: void setResult(String resString); or String getDataToAnalyze();. In that way, controller knows only what pieces of information are displayed in the view, and not how are they displayed. I think it is more flexible (it is easier to change the view layer later).
I know that iOS has some specific rules, has introduced XIB/NIB files etc so maybe my doubts are completely useless in case of iPhone/iPad development. But I am going to write some more serious application for iOS (actually "rewrite" it from Java Swing) and that's why I would like to ask you:
Do you think, I should change the way I am thinking and get accustomed to that new (for me) approach (xib files, creating GUI using drag&drop and providing controler with information about how data should be displayed in view) ?? Did you have similar doubts when starting with iOS?
Short answer:
Yes, I think you should definitely spend a little time getting accustomed to working with Interface Builder (IB) to make NIBs and storyboards and let IB create the IBOutlet and IBAction references for you for those controls with which you need to interact. Once you're proficient at it, you'll be impressed by your productivity in generating easily maintained code. Don't dismiss IB too quickly.
In terms of letting the controller interact directly with the IBOutlet and IBAction references, this is common practice for simple user interfaces. If you have some real world examples, post a new question with a screen snapshot and we can offer more practical guidance.
Long answer:
Part of your question seems to be driven by the apprehension in seeing view controllers that are doing detailed interaction with a view's controls. The thing is, if you want to isolate your controller from some of the implementation details of the view, then go ahead and subclass the view and put the view specific stuff in there. IB can interface with both view controller subclasses as well as view subclasses. So you can happily use IB and still isolate your view controller from some of these implementation details.
Personally, I only do this subclassing of UIView when the view hits some subjective complexity threshold (e.g. for me, that threshold is when I find myself doing some complicated animation, such as using CADisplayLink; complicated gesture recognizers, etc.). I also subclass those subviews that are logical entities of their own (e.g. UITableViewCell or UICollectionViewCell). But for simple views where I'm interacting with my model to setting a control's properties, interacting with text fields, etc., I think putting that in the view controller is fine. Having said that, if I have a lot of view-specific code in my controller which has nothing to do with the integration of my model with my view, then start subclassing the UIView and shifting the view-only code into that.
Implicit in your question is the notion of programmatically building view rather than using NIBs/storyboards. In my opinion, using Interface Builder (IB) to build your UI is much easier to develop and maintain. There might be some pedagogical value to doing a test project where you build your views programmatically, so you really understand what's going on, but after that, I think you'll find yourself quickly gravitating to storyboards. And you'll get plenty of chances to write your own non-IB code when you start doing things beyond the capabilities of the standard IB controls (e.g. complicated custom container views, etc.). There are definitely those who prefer to develop views programmatically, but I don't think you can beat the development speed and ease of maintenance of IB generated UIs.
I general, the controller does not know about the view, but the view knows about the controller.
The gang of four book says:
"MVC also lets you change the way a view responds to user input without changing its visual presentation. You might want to change the way it responds to the keyboard, for example, or have it use a pop-up menu instead of command keys. MVC encapsulates the response mechanism in a Controller object. There is a class hierarchy of controllers, making it easy to create a new controller as a variation on an existing one.
A view uses an instance of a Controller subclass to implement a particular response strategy; to implement a different strategy, simply replace the instance with a different kind of controller. It's even possible to change a view's controller at run-time to let the view change the way it responds to user input. For example, a view can be disabled so that it doesn't accept input simply by giving it a controller that ignores input events.
The View-Controller relationship is an example of the Strategy (315) design pattern. A Strategy is an object that represents an algorithm. It's useful when you want to replace the algorithm either statically or dynamically, when you have a lot of variants of the algorithm, or when the algorithm has complex data structures that you want to encapsulate."
A quick question from a wanna-be iOS developer. I want to create a UI for an iPhone app without Interface Builder, only programmatically. However, I want to stick to MVC recommendations and separate V and C and have a clean readable code, therefore:
I create UIView class files (e.x. SplashView.h and SplashView.m)
I create UIViewController class files (SplashViewController.h and SplashViewController.m)
I define my UI elements (view, subviews, buttons and text fields) in the UIView class files
I load the main view in view controller's loadView method, and then do other things in view controller's viewDidLoad method
Is this a correct approach to begin with?
Second part of the question, independent of Y/N answer to the first. Where do I define these custom UI elements?
- Inside the view's initWithFrame: method?
- In separate (property getter? property setter?) methods? I.e. do I have to declare each UI element as a property first in the .h file?
If these questions sound a bit ignorant, it must be because they are :) I found lots of sample code on StackOverflow, but little to indicate where you actually put it. I would be really grateful for any help, especially if you could paste/reference some relevant code.
Your list is correct. This is how I do all of my apps. No Interface Builder, just code.
Each custom view typically creates its own subviews in an appropriate initXXX method. This could be initWithFrame: but you could define others as needed. Subview layout can be done through constraints, autoresizing masks, or by implementing layoutSubview.
Each view controller would instantiate its needed views in the viewDidLoad. View layout can be done with constraints, autoresizing masks, or by implementing viewWillLayoutSubviews.
The use of properties is completely optional. Create public properties for anything to be set/get from an outside class. Optionally create private properties for values internal to the implementation.
Go to the Apple website for Sample Code; download everything that you can for applications that are similar to your goal.
I'd like to add some custom buttons to an ABUnknownPersonView. Can I use initWithNibName:bundle: in my ABUnknownPersonViewController to load a custom view that I've created in IB, while not using an "undocumented api?" And if I do, how can I make sure that it follows all the properties and responds to all the hooks that the controller expects?
I typically create all my views programmatically and I generally like the view that ABUnknownPersonViewController creates. I'd rather just start from there. And so I accessed the view and dropped in a button, but with later versions of iOS that broke since that isn't a hook that Apple created.
initWithNibName is also not mentioned in the ABUnknownPersonViewController documentation but it is a method of it's parent class UIViewController. Does that make it safe to use?
You cannot provide a replacement XIB. While I understand you want to avoid it, you will have to use the underlying AddressBook framework and building your own version out of it.
You can, of course, look at open source stuff such as this for a good starting point.
I've got an app that I've developed for the iPhone, but now want to port to the iPad. The iPhone app is navigation style and based on discrete table view controllers managed by a nav controller. The larger screen real estate of the iPad means that I can comfortably fit a couple of these table view controllers on to the screen at the same time.
The question is how? Should I
a) have the main view load two table view controllers from separate NIBs and then position them on screen (I'm not sure how I set they x and y of subviews loaded from nibs).
b) create sub-views in my main nib and populate these with data from my existing classes (if so how do I hook up the IBOutlets)?
c) do something completely different
One thing I should point out is that I don't want to use the split screen option.
Alert! This QA is now of historic value only.
It is now trivial to do this sort of thing with container views in iOS, which is why Apple edited them:
https://stackoverflow.com/a/25910881/294884
How to add a subview that has its own UIViewController in Objective-C?
Historic answer...
".. how I set they x and y of subviews loaded from nibs?"
I'm not sure if I fully understand your question Phil, but here's an easy and clear way:
Fire up interface builder and in the new larger iPad view, simply add new smaller views (UIViews)... Put them exactly where and how you want them. We are going to call these "basket" views.
Let's say one of your complicated views from the other app is your fatDogs view. Call the new basket view fatDogsBasket. Then in the code, in viewDidLoad, just do the following with all these "baskets"...
[fatDogsBasket addSubview:fatDogs.view];
[clientsBasket addSubview:clients.view];
[namesBasket addSubview:names.view];
[flashingLightsBasket addSubview:flashingLights.view];
// etc
You're done! (Obviously, make sure that the relevant view controllers, fatDogs, flashingLights and so on, are all ready to go and instantiated.)
The "basket" system is handy since each one will hold your previous work in one place; usefully you can (say) set overall invisibility or whatever just by touching the baskets. Obviously, if you want to set, or maybe move, the position of a basket in the code, just go
happyBasket.frame = CGRectMake(509,413,
happyBasket.frame.size.width,
happyBasket.frame.size.height);
UIViews in iOS are very lightweight, so it's no problem at all adding another layer of UIViews.
I hope this is what you were getting at!
------Later...
You went on to ask: "Just to make sure I'm clear on the right way to implement this. The main view controller has IBOutlets for each of the 'baskets' and its this IBOutlet connection to the subview that I'm calling. Each of the view controllers that I'm going to show in each basket has it's own nib and associated IBOutlets. Right? –"
So, "The main view controller has IBOutlets for each of the 'baskets'"...
Right, the main view in the new app, would have lines like this in the .h file:
IBOutlet UIView *fatDogsBasket;
Note that you are simply declaring "fastDogsBasket" to be a UIView. You shouldn't worry too much about the "IBOutlet" word. All that means is "I need to be able to look this item up, over in the interface controller." It's important to realise IT DOES NOTHING.
So yes all the "baskets" will be UIViews and hence of course you must delare them as such in the .h file of your main view controller. Personally is would not use the phrase "a view controller has IBOutlets." It sort of confuses things and gives the wrong idea. Just say "don't forget to mark the UIViews as iboutlets in the header file."
So anyway yes that's exactly what you do, declare all the "basket" UIViews in the .h file of the main controller, and indeed mark them all as IBOutlets so that interface builder will work more easily. Next ..
"its this IBOutlet connection to the subview that I'm calling" -- that's wrong.
The basket such as fatDogsBasket IS SIMPLY A UIVIEW and that's that. It's just a UIView.
Now, as you know you can put UIViews inside other UIViews. (Obviously, this is commonplace, every UIView has scores of UIViews inside it and so on and on - it's the most basic part of building up an interface.)
So, what are you going to put inside your fatDogsBasket uiview? You're going to put in ALL YOUR PREVIOUS WORK on fatDogs! Previously (for the iFone) you wrote a wonderful class - a view controller - called fatDogs. (It may well have even had many subclasses and so on.)
We're now going to take the view from fatDogs (of course, that is fatDogs.view) and literally put it inside fatDogsBasket. (Recall that fatDogsBasket is a UIView.)
So firstly you would have to completely include your amazing class fatDogs (from the old project) in your new project. Click "add existing flies/classes" or something like that...you'll figure it out. Yes, add all the class files, xibs, any subclasses and so on.
Then, simply do this .. in your new super-powerful uber-controller, in viewDidLoad, just do the following with all the "baskets"...
[fatDogsBasket addSubview:fatDogs.view];
[clientsBasket addSubview:clients.view];
[namesBasket addSubview:names.view];
[flashingLightsBasket addSubview:flashingLights.view];
// etc
You're done! Note that the view from fatDogs (ie, fatDogs.view) is now displaying inside of the UIView fatDogsBasket. The class fatDogs will now work completely normally, just as it did in the old days! And incredibly, you can easily (here in your new controller) do things like simply move fatDogsBasket, and it will move the fatDogs view easily all at once, without worrying about the details of fatDogs and it's views.
Finally you ask..
"Each of the view controllers that I'm going to show in each basket has it's own nib and associated IBOutlets."
Exactly correct. When you add your old system "fatDogs" to the new project, you will be adding all of it's xib files and so on. Anyting that happens or doesn't happen inside those classes, to do with perhaps buttons or anything else marked as iboutlets, or anything else, will just still be the same within those classes. I'm pretty sure absolutely NOTHING will change when you use those old classes in your new project.
Just for the record .. "Each of the view controllers that I'm going to show in each basket.." Just to be accurate, you don't really show as such a viewcontroller, you show the view of the viewcontroller (!!). In other words, for fatDogs (a view controller) you will be showing it's view, which is, simply enough, referred to as fatDogs.view. ("view" is, of course, a property of any UIViewController, so you can just say vcName.view and you're done.)
Hope it helps!
And finally you ask .................................
"I've got it compiling OK, but my baskets are showing up empty, i.e. they're not showing the views of the view controllers that I've imported."
Tell is the name of one of your UIViewController classes from the old project, so we can be specific
Let's say you have an old UIViewController called HappyThing. So you will very likely have a file HappyThing.h and a file HappyThing.m and a file HappyThing.xib.
put all those in the new project, you must do so using Add->Existing Files. (Control on one of your current filenames in the list on the left in XCode.)
You will have to do this #import "HappyThing.h" somewhere or other in your new project - either in your Prefix.pch file or at the top of your new UIControllerView
To be clear in HappyThing.h you will have a line of code
#interface HappyThing : UIViewController
In your new UIViewController.h file, you will have to add a new variable, we'll call it xxx,
HappyThing *xxx;
Note that the type of xxx is HappyThing. (Note that as a rule, you would use the naming convention "happyThing" (note the lowercase "h") rather than "xxx", but it's just a variable and I want it to be clear to you that it's just a variable.)
Next! At the moment it's just a variable that is not pointing to anything, it's nothing. (Just as if you said "int x", but then did not actually say "x = 3" or whatever.) So! In your code you have to actually instantiate xxx.
xxx = [[HappyThing alloc] init];
[xxxBasket addSubview:xxx.view];
Note that the first line is what makes an instance of HappyThing come in to existence. And of course, you want to use "xxx" to point to that instance.
The second line puts the view in to the relevant basket! Note that of course what you want is the view associated with xxx (ie, xxx.view) ... remember that xxx is a UIViewController, it is not itself a UIView. The associated UIView is "xxx.view". (The view is literally just a property of xxx.)
Memory management! Note that you used "alloc" to bring xxx in to existence. This means you DO own it, and of course that means YOU DO NOT need to send a retain there. Furthermore, since you do own it, that means You eventually have to RELEASE it. (easy ... [xxx release];)
So simply add the line [xxx release]; to the dealloc routine in your new UIViewController. (Really it won't cause any harm if you forget to do this, but do it anyway.) Conceivably you may want to release it earlier for some reason once you are more comfortable with the process.
(I was just working on a project with a huge number of huge tables, popovers and the like, so I only made them on the fly and got rid of them as soon as possible, to use less memory. But all of that is irrelevant to you at this stage.)
So now you should SEE IT ON THE SCREEN!
Don't forget if you previously had some routine in HappyThing, which you had to call to start it working (perhaps "beginProcessing" or something), you'll have to call that yourself from the new UIViewController. Hence perhaps something like:
xxx = [[HappyThing alloc] init];
[xxxBasket addSubview:xxx.view];
[xxx beginProcessing];
[xxx showAmazingRedFlashingLights]; // or whatever
Finally you asked ...
"When you've use this technique, do you simply include the headers of the imported files in your main view controller, or do you forward class them in some way?"
That was not your problem, your problem was that you were not instantiating it with the line xxx = [[HappyThing alloc] init];. So, good luck!
Regarding the line of code "#class HapyyThing", if you want to simply put it just above the start of the definition of your new UIControllerView. Generally you don't have to if you have your include line in the best place. Anyway it is an unrelated issue. It simply won't compile if your new UIViewController, can't find HappyThing. Enjoy!