UITableView’s Data Source

The process of providing a UITableView with rows in Cocoa Touch is different from the typical procedural programming task. In a procedural design, you tell the table view what it should display. In Cocoa Touch, the table view asks another object – its dataSource – what it should display. In our case, the ItemsViewController is the data source, so it needs a way to store possession data.

In Chapter 2, you used an NSMutableArray to store Possession instances. You will do the same thing in this exercise, but with a little twist. The NSMutableArray that holds the Possession instances will be abstracted into a PossessionStore (Figure 10.5).

Figure 10.5  Homepwner object diagram

Homepwner object diagram

PossessionStore: a singleton

If an object wants to see all of the possessions, it will ask the PossessionStore for the array that contains them. In future chapters, you’ll make the store responsible for performing operations on the array, like reordering, adding, and removing Possessions. It will also be responsible for saving and loading the Possessions from disk.

From the File menu, select New and then New File.... Create a new NSObject subclass and name it PossessionStore.

PossessionStore will be a singleton, just like UIAccelerometer. This means there will only be one instance of this type in the application; if you try and create another instance, the class will quietly return the existing instance instead.

To get the PossessionStore, you will send the class the message defaultStore. Declare this class method in PossessionStore.h.

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​ ​:​ ​N​S​O​b​j​e​c​t​
{​

}​
/​/​ ​N​o​t​i​c​e​ ​t​h​a​t​ ​t​h​i​s​ ​i​s​ ​a​ ​c​l​a​s​s​ ​m​e​t​h​o​d​,​ ​a​n​d​ ​i​s​ ​p​r​e​f​i​x​e​d​ ​w​i​t​h​ ​a​ ​+​ ​i​n​s​t​e​a​d​ ​o​f​ ​a​ ​-​
+​ ​(​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​ ​*​)​d​e​f​a​u​l​t​S​t​o​r​e​;​

@​e​n​d​

When this message is sent to the PossessionStore class, the class will check to see if the instance of PossessionStore has already been created. If the store exists, the class will return the instance. If not, it will create the instance and return it. To do this, you’ll use a global static variable. At the top of PossessionStore.m, create a global static variable to hold the instance of PossessionStore where the class can access it.

#​i​m​p​o​r​t​ ​"​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​.​h​"​
#​i​m​p​o​r​t​ ​"​P​o​s​s​e​s​s​i​o​n​.​h​"​

s​t​a​t​i​c​ ​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​ ​*​d​e​f​a​u​l​t​S​t​o​r​e​ ​=​ ​n​i​l​;​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​

Also in PossessionStore.m, implement +defaultStore, +allocWithZone: and -init so that only one instance of the class can be created.

+​ ​(​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​ ​*​)​d​e​f​a​u​l​t​S​t​o​r​e​
{​
 ​ ​ ​ ​i​f​ ​(​!​d​e​f​a​u​l​t​S​t​o​r​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​t​h​e​ ​s​i​n​g​l​e​t​o​n​
 ​ ​ ​ ​ ​ ​ ​ ​d​e​f​a​u​l​t​S​t​o​r​e​ ​=​ ​[​[​s​u​p​e​r​ ​a​l​l​o​c​W​i​t​h​Z​o​n​e​:​N​U​L​L​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​}​
 ​ ​ ​ ​r​e​t​u​r​n​ ​d​e​f​a​u​l​t​S​t​o​r​e​;​
}​

/​/​ ​P​r​e​v​e​n​t​ ​c​r​e​a​t​i​o​n​ ​o​f​ ​a​d​d​i​t​i​o​n​a​l​ ​i​n​s​t​a​n​c​e​s​
+​ ​(​i​d​)​a​l​l​o​c​W​i​t​h​Z​o​n​e​:​(​N​S​Z​o​n​e​ ​*​)​z​o​n​e​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​s​e​l​f​ ​d​e​f​a​u​l​t​S​t​o​r​e​]​;​
}​

-​ ​(​i​d​)​i​n​i​t​
{​
 ​ ​ ​ ​/​/​ ​I​f​ ​w​e​ ​a​l​r​e​a​d​y​ ​h​a​v​e​ ​a​n​ ​i​n​s​t​a​n​c​e​ ​o​f​ ​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​.​.​.​
 ​ ​ ​ ​i​f​ ​(​d​e​f​a​u​l​t​S​t​o​r​e​)​ ​{​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​R​e​t​u​r​n​ ​t​h​e​ ​o​l​d​ ​o​n​e​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​d​e​f​a​u​l​t​S​t​o​r​e​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

This code is a bit tricky; let’s walk through it. Whenever the message defaultStore is sent to the class PossessionStore, it checks if the defaultStore variable is nil. The first time this message is sent, defaultStore will be nil, and an instance of PossessionStore will be created by calling allocWithZone: and init.

You have overridden allocWithZone: to return the existing defaultStore to protect the PossessionStore’s singleton status. That’s why defaultStore must call [super allocWithZone:nil] instead of [self allocWithZone:nil].

Now, override the retain count methods so that no one can release the defaultStore

-​ ​(​i​d​)​r​e​t​a​i​n​
{​
 ​ ​ ​ ​/​/​ ​D​o​ ​n​o​t​h​i​n​g​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

-​ ​(​v​o​i​d​)​r​e​l​e​a​s​e​
{​
 ​ ​ ​ ​/​/​ ​D​o​ ​n​o​t​h​i​n​g​
}​

-​ ​(​N​S​U​I​n​t​e​g​e​r​)​r​e​t​a​i​n​C​o​u​n​t​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​N​S​U​I​n​t​e​g​e​r​M​a​x​;​
}​

Now you have a singleton.

In PossessionStore.h, give PossessionStore an instance variable to hold an array of Possession instances and declare two more methods:

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

@​c​l​a​s​s​ ​P​o​s​s​e​s​s​i​o​n​;​

@​i​n​t​e​r​f​a​c​e​ ​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​ ​:​ ​N​S​O​b​j​e​c​t​
{​
 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​*​a​l​l​P​o​s​s​e​s​s​i​o​n​s​;​
}​
+​ ​(​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​ ​*​)​d​e​f​a​u​l​t​S​t​o​r​e​;​

-​ ​(​N​S​A​r​r​a​y​ ​*​)​a​l​l​P​o​s​s​e​s​s​i​o​n​s​;​
-​ ​(​P​o​s​s​e​s​s​i​o​n​ ​*​)​c​r​e​a​t​e​P​o​s​s​e​s​s​i​o​n​;​

@​e​n​d​

In PossessionStore.m, create an instance of NSMutableArray and assign it to the instance variable in the init method.

-​ ​(​i​d​)​i​n​i​t​
{​
 ​ ​ ​ ​i​f​ ​(​d​e​f​a​u​l​t​S​t​o​r​e​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​r​e​t​u​r​n​ ​d​e​f​a​u​l​t​S​t​o​r​e​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​]​;​
 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​=​ ​[​[​N​S​M​u​t​a​b​l​e​A​r​r​a​y​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

Now implement the two methods in PossessionStore.m.

-​ ​(​N​S​A​r​r​a​y​ ​*​)​a​l​l​P​o​s​s​e​s​s​i​o​n​s​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​a​l​l​P​o​s​s​e​s​s​i​o​n​s​;​
}​

-​ ​(​P​o​s​s​e​s​s​i​o​n​ ​*​)​c​r​e​a​t​e​P​o​s​s​e​s​s​i​o​n​
{​
 ​ ​ ​ ​P​o​s​s​e​s​s​i​o​n​ ​*​p​ ​=​ ​[​P​o​s​s​e​s​s​i​o​n​ ​r​a​n​d​o​m​P​o​s​s​e​s​s​i​o​n​]​;​

 ​ ​ ​ ​[​a​l​l​P​o​s​s​e​s​s​i​o​n​s​ ​a​d​d​O​b​j​e​c​t​:​p​]​;​

 ​ ​ ​ ​r​e​t​u​r​n​ ​p​;​
}​

Implementing data source methods

In ItemsViewController.m, import PossessionStore.h and Possession.h and update the designated initializer to add 10 random possessions to the PossessionStore.

#​i​m​p​o​r​t​ ​"​I​t​e​m​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​.​h​"​
#​i​m​p​o​r​t​ ​"​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​.​h​"​
#​i​m​p​o​r​t​ ​"​P​o​s​s​e​s​s​i​o​n​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​I​t​e​m​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​

-​ ​(​i​d​)​i​n​i​t​
{​
 ​ ​ ​ ​/​/​ ​C​a​l​l​ ​t​h​e​ ​s​u​p​e​r​c​l​a​s​s​'​s​ ​d​e​s​i​g​n​a​t​e​d​ ​i​n​i​t​i​a​l​i​z​e​r​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​S​t​y​l​e​:​U​I​T​a​b​l​e​V​i​e​w​S​t​y​l​e​G​r​o​u​p​e​d​]​;​

 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​f​o​r​ ​(​i​n​t​ ​i​ ​=​ ​0​;​ ​i​ ​<​ ​1​0​;​ ​i​+​+​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​[​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​ ​d​e​f​a​u​l​t​S​t​o​r​e​]​ ​c​r​e​a​t​e​P​o​s​s​e​s​s​i​o​n​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

-​ ​(​i​d​)​i​n​i​t​W​i​t​h​S​t​y​l​e​:​(​U​I​T​a​b​l​e​V​i​e​w​S​t​y​l​e​)​s​t​y​l​e​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​s​e​l​f​ ​i​n​i​t​]​;​
}​

Now that there are some possessions in the store, you need to teach ItemsViewController how to turn those possessions into rows that its UITableView can display. When a UITableView wants to know what to display, it uses a set of messages declared in the UITableViewDataSource protocol.

From the Help menu, choose Documentation and API Reference to open the iOS SDK documentation. Find the UITableViewDataSource protocol documentation (Figure 10.6).

Figure 10.6  UITableViewDataSource protocol documentation

UITableViewDataSource protocol documentation

There are many methods here, but notice the two marked required method. For ItemsViewController to conform to UITableViewDataSource, it must implement tableView:numberOfRowsInSection: and tableView:cellForRowAtIndexPath:. These methods tell the table view how many rows it should display and what content to display in each row.

Whenever a UITableView needs to display itself, it sends a series of messages (the required methods plus any optional ones that have been implemented) to its dataSource. The required method tableView:numberOfRowsInSection: returns an integer value for the number of rows that the UITableView should display. In the table view for Homepwner, there should be a row for each entry in the store (Figure 10.7).

Figure 10.7  Obtaining the number of rows

Obtaining the number of rows

Implement tableView:numberOfRowsInSection: in ItemsViewController.m.

-​ ​(​N​S​I​n​t​e​g​e​r​)​t​a​b​l​e​V​i​e​w​:​(​U​I​T​a​b​l​e​V​i​e​w​ ​*​)​t​a​b​l​e​V​i​e​w​
 ​n​u​m​b​e​r​O​f​R​o​w​s​I​n​S​e​c​t​i​o​n​:​(​N​S​I​n​t​e​g​e​r​)​s​e​c​t​i​o​n​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​[​[​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​ ​d​e​f​a​u​l​t​S​t​o​r​e​]​ ​a​l​l​P​o​s​s​e​s​s​i​o​n​s​]​ ​c​o​u​n​t​]​;​
}​

Wondering about the section that this method refers to? Table views can be broken up into sections, and each section has its own set of rows. For example, in the address book, all names beginning with D are grouped together in a section. By default, a table view has one section, and for this exercise, we will work with only one. Once you understand how a table view works, it’s not hard to use multiple sections. In fact, it’s one of the challenges at the end of this chapter.

The second required method in the UITableViewDataSource protocol is tableView:cellForRowAtIndexPath:. To implement this method, we’ll need to learn about another class – UITableViewCell.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.5.201