patternMinor
iOS7 ChessGame UIChessboardView Design
Viewed 0 times
uichessboardviewchessgameios7design
Problem
I've been designing UIChessboardView in the likeness of UITableView using protocols and delegates.
UIChessboardView.h
UIChessboardView.m
```
@implementation UIChessboardView
{
NSDictionary* chessTileViews;
NSArray *tileKeys;
}
-(id)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame])
{
tileKeys = @[@"A1",@"A2",@"A3",@"A4",@"A5",@"A6",@"A7",@"A8",
@"B1",@"B2",@"B3",@"B4",@"B5",@"B6",@"B7",@"B8",
@"C1",@"C2",@"C3",@"C4",@"C5",@"C6",@"C7",@"C8",
@"D1",@"D2",@"D3",@"D4",@"D5",@"D6",@"D7",@"D8",
@"E1",@"E2",@"E3",@"E4",@"E5",@"E6",@"E7",@"E8",
@"F1",@"F2",@"F3",@"F4",@"F5",@"F6",@"F7",@"F8",
@"G1",@"G2",@"G3",@"G4",@"G5",@"G6",@"G7",@"G8",
@"H1",@"H2",@"H3",@"H4",@"H5",@"H6",@"H7",@"H8"];
[self buildChessTileViews];
[self createBackgroundWithImageNamed:@"darkwoodenback.png"];
}
return self;
}
-(void) buildChessTileViews
{
NSMutableArray *tiles = [[NSMutableArray alloc] init];
CGFloat tileHeight = self.frame.size.height/8;
CGFloat tileWidth = self.frame.size.width/8;
int index;
BOOL darkBackround = false;
for(int row = 0; row < 8; row++)
{
for(int column = 0; column < 8; column++)
{
index = (row*8)+column;
UIView tileView = [[UIView alloc] initWithFrame:CGRectMake((columntileHeight), (row*tileW
UIChessboardView.h
@interface UIChessboardView : UIView
@property (retain) id chessdelegate;
-(void) selectChesstileWithID:(NSInteger) tileID;
-(void) deselectChesstileWithID:(NSInteger) tileID;
@end
@protocol ChessboardViewDelegate
-(UIImage*) chessboardView:(UIChessboardView*)chessboardView imageForChesstile:(NSInteger) tileID;
-(void)chessboardView:(id)chessboardView mouseDown_chessTileWithID:(NSInteger)tileID;
-(void)chessboardView:(id)chessboardView mouseUp_chessTileWithID:(NSInteger)tileID;
@endUIChessboardView.m
```
@implementation UIChessboardView
{
NSDictionary* chessTileViews;
NSArray *tileKeys;
}
-(id)initWithFrame:(CGRect)frame
{
if(self = [super initWithFrame:frame])
{
tileKeys = @[@"A1",@"A2",@"A3",@"A4",@"A5",@"A6",@"A7",@"A8",
@"B1",@"B2",@"B3",@"B4",@"B5",@"B6",@"B7",@"B8",
@"C1",@"C2",@"C3",@"C4",@"C5",@"C6",@"C7",@"C8",
@"D1",@"D2",@"D3",@"D4",@"D5",@"D6",@"D7",@"D8",
@"E1",@"E2",@"E3",@"E4",@"E5",@"E6",@"E7",@"E8",
@"F1",@"F2",@"F3",@"F4",@"F5",@"F6",@"F7",@"F8",
@"G1",@"G2",@"G3",@"G4",@"G5",@"G6",@"G7",@"G8",
@"H1",@"H2",@"H3",@"H4",@"H5",@"H6",@"H7",@"H8"];
[self buildChessTileViews];
[self createBackgroundWithImageNamed:@"darkwoodenback.png"];
}
return self;
}
-(void) buildChessTileViews
{
NSMutableArray *tiles = [[NSMutableArray alloc] init];
CGFloat tileHeight = self.frame.size.height/8;
CGFloat tileWidth = self.frame.size.width/8;
int index;
BOOL darkBackround = false;
for(int row = 0; row < 8; row++)
{
for(int column = 0; column < 8; column++)
{
index = (row*8)+column;
UIView tileView = [[UIView alloc] initWithFrame:CGRectMake((columntileHeight), (row*tileW
Solution
@property (retain) id chessdelegate;There are several things wrong with this line.
retain- While this technically still is a property attribute, it has been replaced bystrong. It does the exact same thing asstrong, butstrongis the vastly preferred term here.
retainorstrongare both wrong here anyway. Any delegate property should ALWAYS beweakorassign(andweakis better). In almost every instance, the object server as a delegate already has a strong reference to the object with the delegate property. If we create a strong reference back, we're creating a retain cycle and the memory will never be able to be deallocated.
idworks as the type, technically, but we don't want to use that. What we actually want is something that conforms to the protocol we've outlined. We need to either forward declare the protocol, or probably better, just move the entire protocol definition before the class interface, and then change that type toid.
chessdelegate- First, justdelegateis more than fine. Since we've changed the type toid, we know what kind of delegate it is... the only reason to give the property a name more descriptive than justdelegateis if the class has and needs more than one type of delegate. AND, if you find yourself in this situation, please don't forget to camelcase the property. But in the end, it should just bedelegate.
So, here's what the line should actually look line:
@property (weak) id delegate;mouseDown, mouseUp, and click. We're developing an iOS project, not OSX, right? All iOS devices are touch devices. Moreover, look at the UIView method you override: touchesBegan:withEvent:. There is mouse in iOS and our method names should reflect that.Now then, as for the actual meat of this review and your code, what are we actually looking at?
This is a UI element meant to represent a chessboard. Apple is quite consistent with the Foundation classes in Cocoa Touch. Classes, even unrelated classes, will have similarly named methods that provide similar functionality when it makes sense to do so.
Let's consider
UITableView and UICollectionView for a moment (and I'm choosing these for a reason). Look at the similarities between these objects--specifically, how their delegate/datasource methods work.What does the actual view itself actually do? It only does two things. First, it asks its datasource for what it should draw at a particular index path (and then it takes care of displaying it). Second, it receives the touch events, then calls a method on its delegate to give it an opportunity to respond to the touch event.
Remember when I said that we should rename the delegate to simply
delegate and that the only reason it should be different is if you have multiple? Well, it seems that in keeping with Apple's design for this sort of stuff, it makes sense that we would have both a delegate protocol and a datasource protocol, and as such, we should have a property for each:@property (weak) id delegate;
@property (weak) id datasource;Now, we the chessboard view detects a touch event, it lets the delegate know and give the delegate a chance to respond to this event. Meanwhile, the chessboard should have a public method called
reloadData (exactly as UITableView and UICollectionView have). When this method is called, the chessboard view goes through a series of calls to its datasource to determine what to draw at each square.The chessboard view itself can maintain some information like background image, background colors, how to draw the squares, borders, etc., and it can even have information for what a knight looks like or what a pawn or king, etc looks like, but it should call to a datasource to say "What occupies this square?" 64 times. And with this in mind, it shouldn't even care about any of the chess rules. If I want to put 64 black kings on the board, the chessboard view should be fine with it.
So here's how the program flow should work with this setup:
- First, we load the chessboard. Next, we "reload" data, this is the first time we call to the datasource. We ask the datasource what the board position should look like. This first call might be a new game of chess, or perhaps loading a chess puzzle, or loading a previously saved game.
- Next, we wait for user input. Once we have some user input, we call out to the delegate. The delegate performs some logic. Is this a valid move or not? Do we need to calculate the AI's response to this move? Anyway, the delegate is informed of the move that's attempting to be made.
- Once we've determined the result of the attempted move, we update the data model, and we ask the chessboard view to reload. We the view reloads via calling out to the datasource, which has updated data, so the board will be drawn differently, and we're back to step two of waiting for user input. Step 2 and 3 repeat in a cycle unti
Code Snippets
@property (retain) id chessdelegate;@property (weak) id<ChessboardViewDelegate> delegate;@property (weak) id<ChessboardViewDelegate> delegate;
@property (weak) id<ChessboardviewDatasource> datasource;typedef NS_ENUM(NSInteger, ChessboardViewSquareState) {
ChessboardViewSquareStateUnoccupied,
ChessboardViewSquareStateWhitePawn,
ChessboardViewSquareStateWhiteRook,
ChessboardViewSquareStateWhiteKnight,
ChessboardViewSquareStateWhiteBishop,
ChessboardViewSquareStateWhiteQueen,
ChessboardViewSquareStateWhiteKing,
ChessboardViewSquareStateBlackPawn,
ChessboardViewSquareStateBlackRook,
ChessboardViewSquareStateBlackKnight,
ChessboardViewSquareStateBlackBishop,
ChessboardViewSquareStateBlackQueen,
ChessboardViewSquareStateBlackKing
}@required - (ChessboardViewSquareState)chessboardView:(ChessboardView *)chessboardView stateForSquare:(ChessboardSquare)square;Context
StackExchange Code Review Q#51670, answer score: 8
Revisions (0)
No revisions yet.