I am about to create a Cocoa app and I want to ensure that one day I can easily port it to the iPad or even the iPhone. How can I plan for this in advance?
I know I will have to redo all NIBs and probably design a different workflow.
But what about the code? Just replacing every NSsomething with UIsomething won't cut it, right? Any tips on how to make sure now that I won't shoot myself in the foot later?
Thank you!
(The iPad-SDK is under NDA. For the sake of this question just assume i asked about the iPhone, OK? Or think iPhone with a bigger screen.)
Make sure you strictly uphold the Model-View-Controller separation in your app. The model, especially, should never depend on any controller or view.
In porting to the iPhone/iPod touch/iPad, you'll need to replace most or all of the controllers and all of the NSViews and NSCells. You should be able to keep your CALayer subclasses, if you have any. You may be able to reuse one or two controllers with conditional compilation, if most of a controller will work on both but some parts only work on the Mac or work on both but with completely different APIs. And you should be able to keep the entire model unchanged.
There are probably some more specific pitfalls that an iPhone developer can warn you about, but this is the general rule that holds for any transition from one environment to another. (An example of another environment transition would be making one or more command-line tool equivalents or complements to your app, like xcodebuild, packagemaker, or ibtool.)
Also, look at the Introduction to the Foundation framework reference for figures showing which Foundation classes are Mac- and iPhone-only.
A lot of libraries aren't even supported in Cocoa Touch versus the desktop Cocoa libraries. You need to consider the differences in AppKit versus UIKit. Also, Objective-C does not allow garbage collection on the iPhone. There are many touch events that only exist on the iPhone but not on a desktop. iPhone development is much more restrictive due to the fact that a phone is a very personal device tied to very personal data.
Check out these slides for a better comparison: http://www.slideshare.net/lukhnos/between-cocoa-and-cocoa-touch-a-comparative-introduction
As with any good project layout, you should separate out your UI from your non-UI components. That doesn't just mean on disk layout (though that makes sense as well) but rather using a MVC approach, whereby your controllers (C) know about the Models (M) and the UI (V) is rendered separately.
You can use Key-Value Observing (aka KVO) to set up your models, such that when they fire, it sends around a notification to any registered listeners to update. If you are using XIB to generate your user interfaces, then this is what happens when you bind an object to your display widget(s).
So you can end up with separate XIBs for your iPhone, Mac OS and (later) iPad - though if you resize things correctly, you can have the same XIB for the iPhone and iPad.
Lastly, there are often some cases where you need to inject logic into your models (for example, adding an image to return from a method). In this case, the iPhone and Mac OS have different Image classes. To do this, you can create the following:
MyModel.m // contains the data, no UI
MyModel+UIImage.m // contains a category for adding the UIImage
MyModel+NSImage.m // contains a category for adding the NSImage
The category looks like this:
#interface Host(UIImage)
-(UIImage *)badge;
#end
#implementation MyModel(UIImage)
-(UIImage *)badge
{
if (green)
return [UIImage imageNamed:#"green.png"];
if (red)
return [UIImage imageNamed:#"red.png"];
}
#end
---
#interface Host(NSImage)
-(NSImage *)badge;
#end
#implementation MyModel(NSImage)
-(NSImage *)badge
{
if (green)
return [[NSImage alloc] initWithContentsOfFile: #"green.png"];
if (red)
return [[NSImage alloc] initWithContentsOfFile: #"red.png"];
}
#end
This has the added advantage that your unit tests can just load the model (without any image categories being loaded) whereas at runtime your code that needs to process the images (say, in a view controller) can load the model-with-category and load the badge with [model badge] in a transparent way, regardless of which platform it's compiled for.
Related
I have a very intriguing obstacle to overcome. I am trying to display the live contents of a UIView in another, separate UIView.
What I am trying to accomplish is very similar to Mission Control in Mac OS X. In Mission Control, there are large views in the center, displaying the desktop or an application. Above that, there are small views that can be reorganized. These small views display a live preview of their corresponding app. The preview is instant, and the framerate is exact. Ultimately, I am trying to recreate this effect, as cheaply as possible.
I have tried many possible solutions, and the one shown here is as close as I have gotten. It works, however the - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx method isn't called on every change. My solution was to call [cloneView setNeedsDisplay] using a CADisplayLink, so it is called on every screen refresh. It is very near my goal, but the framerate is extremely low. I think that [CALayer renderInContext:] is much too slow.
If it is possible to have two CALayers render the same source, that would be golden. However, I am not sure how to approach this. Luckily, this is simply a concept app and isn't destined for the App Store, so I can make use of the private APIs. I have looked into IOSurface and Quartz contexts, but I haven't been able to solve this puzzle so far. Any input would be greatly appreciated!
iOS and OSX are actually mostly the same underneath at the lowest level. (However, when you get higher up the stack iOS is actually largely more advanced than OSX as it is newer and had a fresh start)
However, in this case they both use the same thing (I believe). You'll notice something about Mission Control. It isolates "windows" rather than views. On iOS each UIWindow has a ".contentID" property and CALayerHost can use to make the render server share the render context between the 2 of them (2 layers that is).
So my advice is to make your views separate UIWindows and get native mirroring for free-(ish). (In my experience the CALayerHost takes over the target layers place with the render server and so if both the CALayerHost and the window are visible the window won't be anymore, only the layer host will be (which the way they are used on OSX and iOS isn't a problem).)
So if you are after true mirroring, 2 copies of it, you'll need to resort to the sort of thing you were thinking about.
1 Option for this is to create a UIView subclass that uses
https://github.com/yyfrankyy/iOS5.1-Framework-Headers/blob/master/UIKit.framework/UIView-Rendering.h#L12
this UIView private method to get an IOSurface for a target view and then using a CADisplayLink once per second get and draw the surface.
Another option which may work (I'm not sure as I don't know your setup or desired effect) is possibly just to use a CAReplicatorLayer which displays a mirror of a CALayer using the same backing store (very fast and efficient + public stable API).
Sorry I couldn't give you a fixed, "this is the answer reply", but hopefully I've given you enough ideas and possibilities to get started.
I've also included some links to things you might find useful to read.
What's the magic behind CAReplicatorLayer?
http://aptogo.co.uk/2011/08/no-fuss-reflections/
http://iphonedevwiki.net/index.php/SBAppContextHostManager
http://iphonedevwiki.net/index.php/SBAppContextHostView
http://iphonedevwiki.net/index.php/CALayerHost
http://iky1e.tumblr.com/post/33109276151/recreating-remote-views-ios5
http://iky1e.tumblr.com/post/14886675036/current-projects-understanding-ios-app-rendering
I currently have an iOS application that was originally developer for the iPhone; it was then decided that it was going to be required for the iPad as well. However, there are not many changes to cater for this (only things such as layout, text size and a few others) - the app is just a bigger version for iPad in reality.
One of the things my app makes use of is a UITableView (with a custom UITableCellView). Each one of these then moves onto a UIView. In order to make it a bit more unique to the iPad, I would like to implement a UISplitView to combine these two, to make better use of the larger screen!
This application was handed to me from a previous developer. A large part (95%) of the UI was coded (and not done in Interface Builder). While I am picking up objective-c, there are still some things which throw me. Therefore, my question to you guys is: would this require a huge change in code if I was to combine my UITableView and UIView into a UISplitView? From what I have seen from playing around with the UISplitView, it doesn't seem so. However, because of all the rest being coded, I think the UISplitView will have to be, is that right? Or can I dump in an xib and string up the coded UITableView and UIView?
Final question, is there a tutorial anyone knows about where they have coded a UISplitView? All of them appear to be using Interface Builder.
I hope that is not too much information!
I am creating a Universal iOs application as part of an assignment (iPad and iPhone :) ).
Naturally, they have a UI which I have been accustomed to create through the NIB files, using the fancy drag and drop schemes. This obviously seems like a great strategy when you are making a dedicated iOS device application.
However, with the universal application, I notice that this strategy can be a challenge since the 2 UIs differ and human error can promote a lack of consistency in the two UI's + double the work!!!
I noticed the solution to the assignment I am doing has the UI created through the AppDelegate file, I have never really done this, and from this stems the questions:
What is the appDelegate files for anyways?
Is it the way to create the UI for the Universal application through the App delegate? Or do you people still create the UI's through the NIB files meticulously for both iPhone and iPad?
P.S: Side question: This assignment requires me to create a Windows based application vs a View based application which is what I have naturally learnt to do. I understand a Windows based App can grow into a view based application and vice versa. However, I do not understand when you should choose to create a Windows based application?
The AppDelegate in Cocoa is your central Singleton that controls the app workflow. It's used by the underlying Framework to start the application, signal runtime envrionment changes and terminate the app. Being a singleton, it's always there and easy to reference ([UIApplication applicationDelegate]) and it loads up your first view controller.
It's generally common to let the application delegate keep refernces to model and controller objects. But what you describe, the whole UI programmed through the appDelegate, is bad style.
No matter if you use NIB's or you code your UI by manually adding UIElements to the view in code, you should do so in ViewController. Generally, the appDelegate will call the first view controller and that viewcontroller will call all view controller afterwards.
Can any one suggest how I can build Universal app for iPad as well iPhone. What all things I should take care of ? How to take care of resource images used ? Is it possible to have same code base should work for iPad as well iPhone.
In the Target->Project->getInfo->Build->target family-> select iPhone/iPad
And make the conditions everywhere whereever you set frame and also the resolution of the image required by iPAD is high.. so as per condition check whether its running on iPad or iPhone and based on that set your frame and image.
hAPPY cODING...
After creating your universal app (see #Suriya's post above) you should figure out in the app delegate whether you have an iPad or iPhone. Here is a simple tutorial to do just that.
Yes, you will need separate nibs and images for an iPad app. But, no, not all the code has to change. You can simply use class inheritance.
Example:
You have a MyTableViewController.h and .m file that work on the iPhone. This table has a custom cell, MyTableViewCell. Now you want your iPad app to get the same information, but display a larger table and a larger table cell. You then subclass your iPhone classes like so: MyiPadTableViewController : MyTableViewController and MyiPadTableViewCell : MyTableViewCell . This way you have access to all of your created functions in the parent class, but you can override how the information is displayed.
If you have a function - (void)doSomething:(id)foo; in your MyTableViewController class, you can use it in your MyiPadTableViewController class without writing any extra code, or override it if necessary. The point is you don't have to change code in two places, so it makes life a lot easier.
This is Cocoa Touch (et al), iPhone, XCode only.
After completing my first commercial iPhone app, I'm struggling a bit to find a way to start and expand an app from scratch which gives the most linear development (i.e., the least scrapping, re-write or re-organization of code, classes and resources) as app specs change and I learn more (mostly about what Cocoa Touch and other classes and components are designed to be capable of and the limitation of their customization).
So. File, New Project. Blank window based app? Create the controllers I need, with .xib if necessary, so I can localize them and do changes requested by the customer in IB? And then always subclass each class except those extremely unlikely to be customized? (I mean framework classes such as UIButton, CLLocation etc here.)
The question is a generic 'approach' type question, so I'll be happy to listen to handy dev practices you've found paid off. Do you have any tips for which 're-usable components' you've found have become very useful in subsequent projects?
Clients often describe programs in terms of 'first, this screen appears, and then you can click this button and on the new screen you can select... (and so on)' terms. Are there any good guides to go from there to vital early-stage app construction choices, i.e. 'functions-features-visuals description to open-ended-app-architecture'?
For example, in my app I went from NavBar, to Toolbar with items, to Toolbar with two custom subviews in order to accommodate the functions-features-visuals description. Maybe you have also done such a thing and have some advice to offer?
I'm also looking for open-ended approaches to sharing large ("loaded data") objects, or even simple booleans, between controllers and invoking methods in another controller, specifically starting processes such as animation and loading (example: trigger a load from a URL in the second tab viewcontroller after making sure an animation has been started in the first tab viewcontroller), as these two features apply to the app architecture building approach you advocate.
Any handy pointers appreciated. Thanks guys.
Closing this up as there's no single correct answer and was more suitable for the other forum, had I known it existed when I asked :)
If you want to know the method I ended up with, it's basically this:
Window-based blank app
Navigation Controller controls all, whether I need to or not (hide when not used)
Tab Bar Controller if necessary
Connect everything <-- unhelpful, I know.
Set up and check autorotation, it might get added to some view later.
Add one viewcontroller with xib for each view, you never know when they want an extra button somewhere. It's easier to copy code than make the max ultra superdynamic adjustable tableviewcontroller that does all list-navigation, etc.
Re-use a viewcontroller only when just the content differs in it, such as a detail viewcontroller.
Minimize code in each viewcontroller by writing functions and methods and shove them in a shared .m
Everything that's shared ends up in the App delegate, except subclassed stuff.
Modal viewcontrollers are always dynamically created and never have an xib.