How to create Multiple Themes/Skins for iphone apps? [closed] - objective-c

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
I have an iphone app ready and approved by the app store. Now I want to create different themes for my app. Can someone please help me out, with info/links/steps on how to create themes for my app?
I want to create a Metal theme for the Boys and a Pink theme for the Girls. Again by theme I mean, the whole app(features and functionality) is gonna stay the same, but depending on who the user is(boy or girl), he/she can choose the theme they wish to see. And when the theme changes, only the images/Background/music will change according to the applied theme.
Thanks a lot!

This is quite difficult as apps don't have the equivalent of a css stylesheet.
First you need to work out what parts of the app you want to skin, and when you want to allow the user to swap skins.
I'm going to assume that you want to change images and font colours, and that it's okay if the user has to relaunch the app to change the skin (that will make things simpler for now).
Create a plist containing all your skinnable images and colours. The plist will be a dictionary with sensible, theme neutral key names for the images and colours (e.g. don't have a colour called "red", call it "primaryHeadingColor"). Images will be file names, and colours can be hex strings, e.g. FF0000 for red.
You'll have one plist for each theme.
Create a new class called ThemeManager and make it a singleton by adding the following method:
+ (ThemeManager *)sharedManager
{
static ThemeManager *sharedManager = nil;
if (sharedManager == nil)
{
sharedManager = [[ThemeManager alloc] init];
}
return sharedManager;
}
The ThemeManager class will have an NSDictionary property called "styles", and in the init method you will load the theme into your styles dictionary like this:
- (id)init
{
if ((self = [super init]))
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *themeName = [defaults objectForKey:#"theme"] ?: #"default";
NSString *path = [[NSBundle mainBundle] pathForResource:themeName ofType:#"plist"];
self.styles = [NSDictionary dictionaryWithContentsOfFile:path];
}
return self;
}
(Note: some people don't like doing a lot of work inside an init method. I've never found it to be an issue, but if you prefer, create a separate method to load the themes dictionary and call it from your app's setup code).
Notice how I'm getting the name for the theme plist from user defaults. That means the user can select a theme in your preferences and save it and the app will load that theme next time it is launched. I've put in a default theme name of "default" if no theme is selected, so make sure you have a default.plist theme file (or change the #"default" in the code to whatever your default theme plist is actually called).
Now that you've loaded your theme you need to use it; I'm assuming your app has various images and text labels. If you're loading and laying those out in code then this part is easy. If you are doing it in nibs then it's a bit trickier but I'll explain how to handle that later.
Now normally you would load an image by saying:
UIImage *image = [UIImage imageNamed:#"myImage.png"];
But if you want that image to be themable, you'll now need to load it by saying
NSDictionary *styles = [ThemeManager sharedManager].styles;
NSString *imageName = [styles objectForKey:#"myImageKey"];
UIImage *image = [UIImage imageNamed:imageName];
That will look in your theme file for the themed image that matches the key "myImageKey" and will load it. Depending on which theme file you've loaded you'll get a different style.
You'll be using those three lines a lot so you may want to wrap them up in a function. A great idea would be to create a category on UIImage that declares a method called something like:
+ (UIImage *)themeImageNamed:(NSString *)key;
Then to use it you can just replace any calls to [UIImage imageNamed:#"foo.png"]; with [UIImage themeImageNamed:#"foo"]; where foo is now the theme key instead of the actual image name.
Okay, so that's it for theming your images. To theme your label colours, suppose you're currently setting your label colours by saying:
someLabel.color = [UIColor redColor];
You would now replace that with:
NSDictionary *styles = [ThemeManager sharedManager].styles;
NSString *labelColor = [styles objectForKey:#"myLabelColor"];
someLabel.color = [UIColor colorWithHexString:labelColor];
Now you may have noticed that UIColor doesn't have a method "colorWithHexString:" - you'll have to add that using a category. You can Google for "UIColor with hex string" solutions to find code to do that, or I've written a handy category that does that and a bit more here: https://github.com/nicklockwood/ColorUtils
If you've been paying attention you'll also be thinking that instead of writing those three lines over and over, why not add a method to UIColor called:
+ (UIColor *)themeColorNamed:(NSString *)key;
Just like we did with UIImage? Great idea!
So that's it. Now you can theme any image or label in your app. You could use the same trick to set the font name, or any number of other potentially themable visual properties.
There's just one tiny thing we've forgotten...
If you've built most of your views as nibs (and I see no reason why you wouldn't) then these techniques aren't going to work because your image names and font colours are buried inside impenetrable nib data and aren't being set in your source code.
There are a few approaches to solve this:
1) You could make duplicate themed copies of your nibs and then put the nib names in your theme plist and load them from your theme manager. That's not too bad, just implement the nibName method of your view controllers like this:
- (NSString *)nibName
{
NSDictionary *styles = [ThemeManager sharedManager].styles;
return [styles objectForKey:NSStringFromClass([self class])];
}
Notice my neat trick of using the class name of the view controller as the key - that will save you some typing because you can just make a base ThemeViewController with that method and have all your themable view controllers inherit from it.
This approach does mean maintaining multiple copies of each nib though, which is a maintenance nightmare if you need to change any screens later.
2) You could make IBOutlets for all of the imageViews and labels in your nibs, then set their images and colors in code in your viewDidLoad method. That's probably the most cumbersome approach, but at least you don't have duplicate nibs to maintain (this is essentially the same problem as localising nibs btw, and pretty much the same solution options).
3) You could create a custom subclass of UILabel called ThemeLabel that automatically sets the font color using the code above when the label is instantiated, then use those ThemeLabels in your nib files instead of regular UILabels by setting the class of the label to ThemeLabel in Interface Builder. Unfortunately if you have more than one font or font colour, you'll need to create a different UILabel subclass for each different style.
Or you could be devious and use something like the view tag or accessibilityLabel property as the style dictionary key so that you can have a single ThemeLabel class and set the accessibility label in Interface Builder to select the style.
The same trick could work for ImageViews - create a UIImageView subclass called ThemeImageView that, in the awakeFromNib method replaces the image with a theme image based on the tag or accessibilityLabel property.
Personally I like option 3 best because it saves on coding. Another advantage of option 3 is that if you wanted to be able to swap themes at runtime, you could implement a mechanism where your theme manager reloads the theme dictionary, then broadcasts an NSNotification to all the ThemeLabels and ThemeImageViews telling them to redraw themselves. That would probably only take about an extra 15 lines of code.
Anyway, there you have a complete iOS app theming solution. You're welcome!
UPDATE:
As of iOS 5, it's now possible to set custom attributes by keyPath in Interface Builder, meaning that it's no longer necessary to create a view subclass for each themable property, or abuse the tag or accessibilityLabel for selecting styles. Just give your UILabel or UIImageView subclass a string property to indicate which theme key it should use from the plist, and then set that value in IB.
UPDATE 2:
As of iOS 6, there is now a limited skinning system built into iOS that allows you to use a property called the UIAppearance proxy to skin all instances of a given control class at once (there's a good tutorial about the UIAppearance APIs here). It's worth checking if this is sufficient for your skinning needs, but if not, the solution I outlined above still works well, and can be used instead, or in combination with UIAppearance.

Related

How do I draw a grid of images in Cocoa and export it to a PDF?

I'm making a tool that will pull data from a .csv and create a grid of images with captions [like "This"] in Cocoa, then export that to a PDF. I do not need to actually display the view, just save a file. As a complete beginner to drawing programmatically, I have some questions about the process:
What class should I use? I'm assuming NSView, but like I said I've never done this before so I'm not sure.
Do I need to specify the pixel coordinates for every single object, or can I make each object relative to another in some way?
How do I create separate pages for the view?
Keep in mind that I read the Apple guides, and while it had some helpful tidbits, overall it was unusually hard for me to comprehend. If someone could explain in layman's terms what I need to know it would be very appreciated! Thank you in advance.
Have a look at NSCollectionView
Overview
NSCollectionView class displays an array of content as a grid of
views. The views are specified using the NSCollectionViewItem class
which makes loadings nibs containing the view easy, and supports
bindings
There are lots of tutorials.
Including:
Cocoa Programming L42 - NSCollectionView
And
Apples own quick guide to Collection Views
And maybe also look at NSDocuments
Overview
The NSDocument abstract class defines the interface for OS X
documents. A document is an object that can internally represent data
displayed in a window and that can read data from and write data to a
file or file package. Documents create and manage one or more window
controllers and are in turn managed by a document controller.
Documents respond to first-responder action messages to save, revert,
and print their data.
Conceptually, a document is a container for a body of information
identified by a name under which it is stored in a disk file. In this
sense, however, the document is not the same as the file but is an
object in memory that owns and manages the document data. In the
context of AppKit, a document is an instance of a custom NSDocument
subclass that knows how to represent internally, in one or more
formats, persistent data that is displayed in windows.
A document can read that data from a file and write it to a file. It
is also the first-responder target for many menu commands related to
documents, such as Save, Revert, and Print. A document manages its
window’s edited status and is set up to perform undo and redo
operations. When a window is closing, the document is asked before the
window delegate to approve the closing.
NSDocument is one of the triad of AppKit classes that establish an
architectural basis for document-based apps (the others being
NSDocumentController and NSWindowController).
Figured it out a few days ago, thought I'd come back to answer for anyone else with the same question.
What class should I use? I'm assuming NSView, but like I said I've never done this before so I'm not sure.
NSView is in fact the class I used to draw each page.
Do I need to specify the pixel coordinates for every single object, or can I make each object relative to another in some way?
I did end up specifying the pixel coordinates for each image on the grid (plus its caption), but it was easy to calculate where they should be placed once I learned the size of a 8.50 x 11 inch page in points. The next challenge was drawing them in a for loop rather than having to explicitly declare each possible NSRect. Here's my code in drawRect:
// Declared elsewhere: constants for horizontal/vertical spacing,
// the width/height for an image, and a value for what row the image
// should be drawn on
for (int i = 0; i < [_people count]; i++) {
float horizontalPoint = 0.0; // What column should the image be in?
if (i % 2 != 0) { // Is i odd? (i.e. Should the image be in the right column?)
horizontalPoint += (imageWidth + horizontalSpace); // Push it to the right
}
NSRect imageRect = NSMakeRect(horizontalSpace + horizontalPoint, verticalSpace + verticalPoint,
imageWidth, imageHeight);
// Draw the image with imageRect
if (i % 2 != 0) { // Is i odd? (i.e. Is the current row drawn?)
verticalPoint = (imageRect.origin.y + imageRect.size.height); // Push the row down
}
}
I do realize that I could've coded that more efficiently (e.g. making a BOOL for i % 2 != 0), but I was rushing the whole project because my friend who needed it was on a deadline.
How do I create separate pages for the view?
With some googling, I came up with this SO answer. However, this wasn't going to work unless I had one big view with all the pages concatenated together. I came up with a way to do just that:
// Get an array of arrays containing 1-6 JANPerson objects each using data from a parsed in .csv
NSArray *paginatedPeople = [JANGridView paginatedPeople:people];
int pages = [JANGridView numberOfPages:people];
// Create a custom JANFlippedView (just an NSView subclass overriding isFlipped to YES)
// This will hold all of our pages, so the height should be the # of pages * the size of one page
JANFlippedView *view = [[JANFlippedView alloc] initWithFrame:NSMakeRect(0, 0, 612, 792 * pages)];
for (int i = 0; i < [paginatedPeople count]; i++) { // Iterate through each page
// Create a custom JANGridView with an array of people to draw on a grid
JANGridView *gridView = [[JANGridView alloc] initWithFrame:NSMakeRect(0, 0, 612, 792) people:paginatedPeople[i]];
// Push the view's frame down by 792 points for each page drawn already
// and add it to the main view
gridView.frame = NSMakeRect(0, 792 * i, gridView.frame.size.width, gridView.frame.size.height);
[view addSubview:gridView];
}
I apologize if this is hard to understand for anybody; I'm better at talking through my process than writing! I welcome anyone to ask for help if there's something unclear, or edit if they can make it better.
NsView; so tis a mac app?
CGPointMake Returns a point with the specified coordinates. i.e. placing an image in a specific spot on the screen using matrices i.e.
layer.position = CGPointMake ([self view].bounds.size.width /2, [self view].bounds.size.height /3 );
(this example is oriented around core animation (moving objects on screen so please don't take it too literally) hence the layer attribute)
Also this line
layer.bounds= CGRectMake (100,100,1000,1000);
specifies a rectangles boundaries (rectangles can be filled with images and custom data using a bridge i believe; like this):
UIImage *image2 = [[UIImage alloc]initWithContentsOfFile:[[NSBundle mainBundle]pathForResource:#"flogo#2x"ofType:#"png"]];
layer.contents = (__bridge id)image2.CGImage;
Also i believe the cgdrawrect class when combined with matrices i.e. (x,x,x,x) can draw custom rectangles as in your image.
But hopefully you catch my drift with drawing and substituting images . The Core graphics framework will probably be used here. ( my whole answer used core animation as a reference)

How to create a reusable subview with controls, without using xib file

I'm coding an app, and have several views that are reused (f. ex. containing a textfield, a label and a button)
Now i would like to just create it in a "globalviews.m" file once and add it as a subview to the mainview. (I really don't like the interface builder)
I could easily create a function that returns a UIView, with the components in them, but i would like to access the controls of course.
I was hoping something like (making "searchview" global in the viewcontroller in use)
// making searchview a global thingy
UIView *seachview ;
// rest of code here and then in viewdidload:
UIView *seachview = [[UIView alloc] init] ;
searchview = [[globaviews alloc] thesearchviews_name] ;
[self addsubview:searchview] ;
But how could I make controls inside easily accessible. Like:
NSString *something = searchview.textviewname.text ;
Or would this be a terrible idea to begin with?
Its just the way I would prefer to code...
You can create a custom class that is a subclass of UIView. You could then add properties for each control (the same way you would add NSString, NSNumber etc). Or you could create public methods to modify / get data.
e.g.
- (void)setTextFiledColour:(UIColor *)color;
- (NSString *)getTextFieldText;
My personal opinion (from a lot of experience) is to learn interface builder and deal with it. It is perfectly possible to do what you want and many people agree with you and choose to do it that way. Personally I've never seen it done "right". Meaning that its all done custom to create their own patterns and methodologies, avoiding years of experience and testing that has gone into the patterns provided by interface builder.
I find that storyboards in particular force a very specific pattern and style of coding. I think moving away form that is a huge mistake, as if used correctly it has great potential to abstract away UI / Code, prevents taking shortcuts that come back later on and most importantly when someone else needs to change it, there is no ambiguity, you can't make a mistake with class names or variable names etc.
I've used storyboards a lot and have yet to have an issue with them, on the flip side i've worked with developers who insist on doing it all by hand and have never had so many issues, and shocked at how long it takes to develop applications in this manner.
In the end its up to you.
Note
You mentioned wanting to create a view and reuse it. Its also possible to do this in a .xib file and reuse it that way. Losing some of the storyboard abilities but might give you a little of both worlds.

Keep NSTextView's Font From Changing

I thought this would be straight forward, but it looks like I was wrong. Basically, all I'm trying to do is keep the font from changing to the Apple default: Helvetica Regular 12pt.
I've made a subclass of NSDocument and in my implementation file I have the following method:
- (void)windowControllerDidLoadNib:(NSWindowController*)aController
{
[super windowControllerDidLoadNib:aController];
if(attrString)
{
[[textView textStorage] setAttributedString:attrString];
[[textView textStorage] setFont:[NSFont fontWithName:#"Menlo Bold" size:24]];
}
This method works all right when I open a file, but if I delete all of the text and then type again, the font resets to... Helvetica Regular 12pt... All I want is to keep the font and size as I specified it for the entire life of the program.
You need to set the typing attributes of the text view to contain your font for the key NSFontAttributeName.
However, I would go a step further. If you know you NEVER want a certain font in your model (NSTextStorage -- the backing store for NSTextView), simply subclass NSTextStorage and override the attribute setters and getters. NSTextView gives the user access to font menus and copy/paste will still allow certain fonts in. The only way to truly guarantee it never enters your text view is to never allow it into the model.

Can anyone help out an Objective C novice?

I feel i am totally out of my depth here, im very new to objective c but have been asked to design an iphone app as part of my uni course. I designed a sinple quiz before, but I was hoping to design a more advanced quiz game with 3 levels (each level will have a different quiz).
I dont know how to use the UIViews and I have tried a tutorial online to help me code a navigation controller. It allows gives me 3 options to go into subview 1, 2 or 3. All the subviews have the same screen though, with one label and a button.
I have 3 classes so far, RootViewController, BasicNavigationAppDelegate and SubViewOneController.
I really dont understand the code at all, im familiar with Java but this objective c is nothing like it. Could someone maybe take a minute to help out a person in distress and explain if i am doing this right by using the navigation controller to display my levels? When i check the xib interface files i dont see the button or label, or dont know where to add the quiz interface objects!! I really am confused by all this. Could anyone help?
You should search google for sample source code, and see how some of the views can be handled. There are many ways you can handle a view, whether its by a UINavigationController, UITabBarController, etc. If you are new to Objective-C, then your not really going to get an answer to this question that will instruct you on what exactly to do.
Interface Builder + View Controllers
Here's a good one for you: View Controllers Programming Guide
(Apple's) View Controller Reference Guide
Some Code Samples
Getting Started Sample Code
I recommend Head First iPhone Development: A Learner's Guide to Creating Objective-C Applications for the iPhone. Within a few chapters you'll know everything you need to build this app and you'll actually understand what you're doing.
(I don't know the authors or the publisher, I just think it's a great book for quickly getting up to speed.)
for a 3 level quiz, UINavigationController is definitely an option.
if you need to find out how to use a class, in xcode, type its name, then press -alt- and double click the class name, this will bring up a short description, with two icons, one will take you to the header file, and the other to the documentation.
to add elements to the nib/xib files, you will need to open the library window, where you will find labels, buttons etc. to use a button, you will need to define an action in your header file, and hook it up in IB, to be able to interact with UIElements in your code, you want to set up outlets in the header file, and hook them up in IB.
something you need to decide on, is how you are going to present the questions, and will also depend if the answer will be true/false, multiple choice, or text entry.
if you arent familiar with obj-c and xcode, it is probably worth picking up an ebook from someone like http://www.pragprog.com. they have an iPhone one up there by Bill Dudney which is quite good(i believe he now works for apple.)
for the standard slide out transition you could use this.
//you would probably want to call this something like level1NavBarItemWasPushed: instead
- (IBAction)lvl1pushNavBarItem:(id)sender {
//create instance of AnswersViewController class.
AnswersViewController *level1AnswersVC= [[Level1AnswersViewController alloc] init];
//pass it some kind of identifier so it can tell which quiz/question it is dealing with and pull in the answers, so that you can reuse the view
[level1AnswersVC setAnswersObject:<<insert object dictionary here>>];
//push the view controller onto the navigationController's view stack
[self.navigationController pushViewController:level1AnswersVC animated:TRUE];
//pushing it onto the view stack has given it +1 retain, so we can now release it without worrying about it disappearing prematurely.
[level1AnswersVC release];
}
for the page flip transition you could use this.
- (IBAction)lvl1pushNavBarItem:(id)sender {
//create instance of AnswersViewController class.
AnswersViewController *level1AnswersVC= [[Level1AnswersViewController alloc] init];
//pass it some kind of identifier so it can tell which quiz/question it is dealing with and pull in the answers, so that you can reuse the view
[level1AnswersVC setAnswersObject:<<insert object dictionary here>>];
//set the current viewController as the delegate, so that it can call back to us when its done
level1AnswersVC.delegate = self;
//set the modal transition style
level1AnswersVC.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
//show answers as modal view, which has been setup to use the page flip transition.
[self presentModalViewController:level1AnswersVC animated:YES];
//pushing it onto the view stack has given it +1 retain, so we can now release it without worrying about it disappearing prematurely.
[level1AnswersVC release];
}

Is it possible to design NSCell subclasses in Interface Builder?

I'm trying to subclass NSCell for use in a NSTableView. The cell I want to create is fairly complicated so it would be very useful if I could design it in Interface Builder and then load the NSCell from a nib.
Is this possible? How do I do it?
The question was about a subclass of NSCell; the other answers seem to be doing something else, likely taking advantage of UITableViewCell being a view.
NSCell is not a view. While laying a custom cell out in IB would be a useful thing to be able to do, I think the answer is basically "no, this is not possible". When you subclass NSCell, you're pretty much just doing your own drawing. There isn't support subcells, or parameterized auto layout (ala NSView's springs and struts), which is I suspect what you're looking for.
The only caveat is that you could design an NSCell subclass that did do layout of sub-elements and provided parameters for setting those subelements and all tweakable parameters. Then, you would need to write an IB plugin to make that cell and accompanying inspector available at design time in IB.
This, however, is probably harder than writing a little custom app that does more or less the same thing. Put an NSCell in a control in the middle of a window, and make yourself UI for tweaking the parameters you're interested in. Bindings can make this pretty straightforward for positioning stuff (i.e. bind an x value to a slider), though you will not get direct manipulation of the elements of course. When you're done, you could archive your cell and load the archive at runtime in your real app, or you could just log out the properties and set them in code in your app.
Some answers in this thread have gone off topic because they're talking about Cocoa Touch, when the original question was about Cocoa - the 2 APIs are quite different in this regard and Cocoa Touch makes it easy because UITableViewCell is a view subclass. NSCell isn't, and that's the problem
For information, I had to do something very similar in NSOutlineView recently - which is basically the same, but a little harder if anything because you have to deal with disclosure / collapse of levels. If you're interested in the code, I posted about it here: http://www.stevestreeting.com/2010/08/08/cocoa-tip-using-custom-table-outline-cells-designed-in-ib/
HTH
As Ken says, NSCells and NSViews are different, and you can only lay out NSView hierarchies in NIB, not NSCells (which don't have any explicit hierarchy).
On the other hand, there's nothing preventing you from having a hierarchy of NSViews and using that to draw your NSCell -- you could add them as a subview of your cell's parent view, tell them to display, and remove them from the window and nobody would be the wiser.
In this case, using a NIB would work, although it seems like a ton of hassle. Typically I've just replaced the object that takes NSCells with a custom one that takes my NSViews, but that means writing your own mouse-handling code, which is very touchy.
On the other hand, my approach lets you bind the views' values in NIB, so you don't have to do any extra work, which is cool.
In IB, start an empty XIB. Now go to the pallete and drag in a UITableViewCell, double click to bring up and edit.
include only the custom UITableViewCell (no other UIViews or other top level controls) - make sure it's a real UITableViewCell in IB, or you cannot set a reuse identifier (as opposed to casting a UIView in IB as your custom UITableViewCell class).
Then you can add lables or whatever you like within the cell, as well as setting the reuse identifier or set whatever disclosure indicator you might like.
To use, you provide code like this in the tableView:cellForRow:atIndexPath: method:
YourCustomCellClass *cell = (YourCustomCellClass *)[tableView dequeueReusableCellWithIdentifier:<IDYouSetInXIBFile>];
if ( cell == nil )
{
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:<YourXIBName> owner:self options:nil];
id firstObject = [topLevelObjects objectAtIndex:0];
if ( [ firstObject isKindOfClass:[UITableViewCell class]] )
cell = firstObject;
else cell = [topLevelObjects objectAtIndex:1];
}
If you have any labels or other controls you want to reference in your code, wire them in IB to your custom cell class - NOT the file's owner, which you do not ever need to set using the above code (you can leave it as NSObject).
Edit: I note you are really looking for an NSCell answer, but the code approach to using IB should be identical in Cocoa with the Cocoa Touch code I used above as loadNibNamed is a standard Cocoa call.
Joar Wingfors wrote an article for Stepwise a few years back on a related topic, Subviews in TableView Rows.
The principal technique is to create an NSCell that can host an NSView. If you were to do this, you could then design an NSView subclass in Interface Builder that you could embed anywhere you need that specific cell.
Another possibility, if you can target Leopard, is to see whether you need to use an NSTableView or whether you can use an NSCollectionView. Collection views deal directly in terms of "item views" rather than in cells, so they're much more straightforward to design in Interface Builder.
I found some interesting examples which I do not totally understand, though.
GitX extends the NSTextFieldCell in their PBIconAndTextCell, referencing this post.
WWDC 2009 - Session 110 "Presenting User Data with Table Views and Browsers" talks "Adding subviews" and "Custom cell editors". (I do not have the source code, though.)
Display an NSTextfieldCell containing text and an image within a NSTableView, #70
Display an NSTextfieldCell containing text and an image within a NSTableView, #71
The last 2 examples work with NSTableViewDataSource and NSTableViewDelegate. I would like to use Bindings an ArrayController in the InterfaceBuilder to connect other UI elements like text fields.
I stumbled into another discussion where Abizern points out PXListView by Alex Rozanski which looks very promising!
I am trying to implement a solution for the problem myself. Please find my project on github and the latest rendering problems over here.
I do it like this:
/* example of a silly way to load a UITableViewCell from a standalone nib */
+ (CEntryTableViewCell *)cell
{
// TODO -- this is really silly.
NSArray *theObjects = [[NSBundle mainBundle] loadNibNamed:#"EntryTableViewCell" owner:self options:NULL];
for (id theObject in theObjects)
if ([theObject isKindOfClass:self])
return(theObject);
NSAssert(NO, #"Could not find object of class CEntryTableViewCell in nib");
return(NULL);
}
However it isn't very efficient and if you're loading lot of data it might hurt you. Of course you should be using a reuseIdentifier which should force this code to only run a handful of times per table.
1) Create NSViewController TableViewCell.h
2) Create in TableViewCell.h some procedures like
-(void)setText:(NSString *)text image:(NSImage *)image
3) In Main class #import "TableViewCell.h"
4) In Main class in -(NSView *)tableView:viewForTableColumn:row: write:
NSImage *img = //some image
TableViewCell *cell = [[TableViewCell alloc] initWithWindowNibName:#"TableViewCell"];
cell.view.init;
[cell setText:#"some text" image:img];
return cell;
Hope this will help =)
I want to provide a more modern approach here.
Starting with iOS 5, UITableView has a method
(void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier
Once you registered your NIB containing your cell, just use
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
to get a new cell. If a cell is available for reuse, it will be returned, otherwise a new cell is automatically created, and in that case this means loaded from the NIB file.
Add your UITableViewCell to your tableviewcontroller and declare an IBOutlet property:
#interface KuguTableViewController : UITableViewController {
IBOutlet UITableViewCell *customTypeCell;
}
#property (readonly) UITableViewCell *customTypeCell;
... then in cellForRowAtIndexPath you can just use your cell and set it to be reused:
static NSString *CellIdentifier = #"CustomCell"
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = customTypeCell;
cell.reuseIdentifier = CellIdentifier;