1、创建BIDTinyPixDocument类
#import <UIKit/UIKit.h> //创建文档类
@interface TinyPixDocument : UIDocument
//接收一对行和列索引作为参数
- (BOOL)stateAtRow:(NSUInteger)row column:(NSUInteger)column;
//指定的行和列设置特定的状态
- (void)setState:(BOOL)state atRow:(NSUInteger)row column:(NSUInteger)column;
//负责切换特定位置处的状态
- (void)toggleStateAtRow:(NSUInteger)row column:(NSUInteger)column; @end
#import "TinyPixDocument.h" //类扩展
@interface TinyPixDocument ()
@property (nonatomic,strong) NSMutableData * bitmap;
@end @implementation TinyPixDocument //将每个位图初始化为从一个角延伸到另一个角的对角线图案。
- (id)initWithFileURL:(NSURL *)url
{
self = [super initWithFileURL:url];
if (self) {
unsigned char startPattern[] = {
0x01,
0x02,
0x04,
0x08,
0x10,
0x20,
0x40,
0x80
}; self.bitmap = [NSMutableData dataWithBytes:startPattern length:];
}
return self;
} //实现读取单个位的状态的方法。实现这个方法只要从字节数组中获取相关字节,然后对其进行位移操作和AND操作,检查是否设置了给定位,相应的返回YES或NO。
- (BOOL)stateAtRow:(NSUInteger)row column:(NSUInteger)column
{
const char * bitmapBytes = [self.bitmap bytes];
char rowByte = bitmapBytes[row];
char result = ( << column) & rowByte;
if (result != ) {
return YES;
} else {
return NO;
}
}
//这个方法正好和上一个相反,用于为给定行和列的位置设置值。
- (void)setState:(BOOL)state atRow:(NSUInteger)row column:(NSUInteger)column
{
char *bitmapBytes = [self.bitmap mutableBytes];
char *rowByte = &bitmapBytes[row]; if (state) {
*rowByte = *rowByte | ( << column);
} else {
*rowByte = *rowByte & ~( << column);
}
}
//辅助方法,外部代码使用该方法来切换单个单元的状态。
- (void)toggleStateAtRow:(NSUInteger)row column:(NSUInteger)column
{
BOOL state = [self stateAtRow:row column:column];
[self setState:!state atRow:row column:column];
} //保存文档时调用
- (id)contentsForType:(NSString *)typeName error:(NSError **)outError
{
NSLog(@"saving document to URL %@", self.fileURL);
return [self.bitmap copy];
}
//系统从存储区加载了数据,并且准备将这个数据提供给文档类的一个实例时,调用此方法。
- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName
error:(NSError **)outError
{
NSLog(@"loading document from URL %@", self.fileURL);
self.bitmap = [contents mutableCopy];
return true;
}
2、主控制器代码
#import "BIDMasterViewController.h"
#import "BIDDetailViewController.h"
#import "BIDTinyPixDocument.h" @interface BIDMasterViewController () <UIAlertViewDelegate> @property (weak, nonatomic) IBOutlet UISegmentedControl *colorControl;
@property (strong, nonatomic) NSArray * documentFilenames;
@property (strong, nonatomic) BIDTinyPixDocument * chosenDocument; @property (strong, nonatomic) NSMetadataQuery * query;
@property (strong, nonatomic) NSMutableArray * documentURLs; @end @implementation BIDMasterViewController /* original
//接收一个文件名作为参数,将它和应用的Document目录的文件路径结合起来,然后返回一个指向该文件的URL指针。
- (NSURL *)urlForFilename:(NSString *)filename {
NSFileManager * fm = [NSFileManager defaultManager];
NSArray * urls = [fm URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask];
NSURL * directoryURL = urls[0];
NSURL * fileURL = [directoryURL URLByAppendingPathComponent:filename];
return fileURL;
}
*/ - (NSURL *)urlForFilename:(NSString *)filename
{
// be sure to insert "Documents" into the path
NSURL * baseURL = [[NSFileManager defaultManager]
URLForUbiquityContainerIdentifier:nil];
NSURL * pathURL = [baseURL URLByAppendingPathComponent:@"Documents"];
NSURL * destinationURL = [pathURL URLByAppendingPathComponent:filename];
return destinationURL;
} /* original //也用到了Document目录,用于查找代表现存文档的文件。该方法获取它所找到的文件,并将它们根据创建的时间来排序,以便用户可以以“博客风格”的顺序来查看文档列表(第一个文档是最新的)。文档文件名被存放在documentFilenames属性中,然后重新加载表视图(我们尚未处理)。
- (void)reloadFiles {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *path = paths[0];
NSFileManager *fm = [NSFileManager defaultManager]; NSError *dirError;
NSArray *files = [fm contentsOfDirectoryAtPath:path error:&dirError];
if (!files) {
NSLog(@"Encountered error while trying to list files in directory %@: %@",
path, dirError);
}
NSLog(@"found files: %@", files); files = [files sortedArrayUsingComparator:
^NSComparisonResult(id filename1, id filename2) {
NSDictionary *attr1 = [fm attributesOfItemAtPath:
[path stringByAppendingPathComponent:filename1]
error:nil];
NSDictionary *attr2 = [fm attributesOfItemAtPath:
[path stringByAppendingPathComponent:filename2]
error:nil];
return [attr2[NSFileCreationDate] compare: attr1[NSFileCreationDate]];
}];
self.documentFilenames = files;
[self.tableView reloadData];
}
*/ - (void)reloadFiles {
NSFileManager * fileManager = [NSFileManager defaultManager];
// passing nil is OK here, matches first entitlement
NSURL * cloudURL = [fileManager URLForUbiquityContainerIdentifier:nil];
NSLog(@"got cloudURL %@", cloudURL); // returns nil in simulator self.query = [[NSMetadataQuery alloc] init];
_query.predicate = [NSPredicate predicateWithFormat:@"%K like '*.tinypix'",
NSMetadataItemFSNameKey];
_query.searchScopes = [NSArray arrayWithObject:
NSMetadataQueryUbiquitousDocumentsScope];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(updateUbiquitousDocuments:)
name:NSMetadataQueryDidFinishGatheringNotification
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(updateUbiquitousDocuments:)
name:NSMetadataQueryDidUpdateNotification
object:nil];
[_query startQuery];
} - (void)updateUbiquitousDocuments:(NSNotification *)notification {
self.documentURLs = [NSMutableArray array];
self.documentFilenames = [NSMutableArray array]; NSLog(@"updateUbiquitousDocuments, results = %@", self.query.results);
NSArray *results = [self.query.results sortedArrayUsingComparator:
^NSComparisonResult(id obj1, id obj2) {
NSMetadataItem *item1 = obj1;
NSMetadataItem *item2 = obj2;
return [[item2 valueForAttribute:NSMetadataItemFSCreationDateKey] compare:
[item1 valueForAttribute:NSMetadataItemFSCreationDateKey]];
}]; for (NSMetadataItem *item in results) {
NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
[self.documentURLs addObject:url];
[(NSMutableArray *)_documentFilenames addObject:[url lastPathComponent]];
} [self.tableView reloadData];
} - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return ;
} - (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [self.documentFilenames count];
} - (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"FileCell"]; NSString * path = self.documentFilenames[indexPath.row];
cell.textLabel.text = path.lastPathComponent.stringByDeletingPathExtension;
return cell;
} - (IBAction)chooseColor:(id)sender {
NSInteger selectedColorIndex = [(UISegmentedControl *)sender selectedSegmentIndex];
[self setTintColorForIndex:selectedColorIndex]; // NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// [prefs setInteger:selectedColorIndex forKey:@"selectedColorIndex"];
// [prefs synchronize]; NSUbiquitousKeyValueStore * prefs = [NSUbiquitousKeyValueStore defaultStore];
[prefs setLongLong:selectedColorIndex forKey:@"selectedColorIndex"];
} - (void)setTintColorForIndex:(NSInteger)selectedColorIndex {
UIColor *tint = nil;
switch (selectedColorIndex) {
case :
tint = [UIColor redColor];
break;
case :
tint = [UIColor colorWithRed: green:0.6 blue: alpha:];
break;
case :
tint = [UIColor blueColor];
break;
default:
break;
}
[UIApplication sharedApplication].keyWindow.tintColor = tint;
} - (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated]; // NSUserDefaults * prefs = [NSUserDefaults standardUserDefaults];
// NSInteger selectedColorIndex = [prefs integerForKey:@"selectedColorIndex"]; NSUbiquitousKeyValueStore * prefs = [NSUbiquitousKeyValueStore defaultStore];
NSInteger selectedColorIndex = (int)[prefs longLongForKey:@"selectedColorIndex"]; [self setTintColorForIndex:selectedColorIndex];
[self.colorControl setSelectedSegmentIndex:selectedColorIndex];
} - (void)viewDidLoad
{
[super viewDidLoad]; UIBarButtonItem * addButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:@selector(insertNewObject)];
self.navigationItem.rightBarButtonItem = addButton;
[self reloadFiles];
} - (void)insertNewObject {
// get the name
UIAlertView *alert =
[[UIAlertView alloc] initWithTitle:@"Filename"
message:@"Enter a name for your new TinyPix document."
delegate:self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"Create", nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
[alert show];
} - (void)alertView:(UIAlertView *)alertView
didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex == ) {
NSString *filename = [NSString stringWithFormat:@"%@.tinypix",
[alertView textFieldAtIndex:].text];
NSURL *saveUrl = [self urlForFilename:filename];
self.chosenDocument = [[BIDTinyPixDocument alloc] initWithFileURL:saveUrl];
[self.chosenDocument saveToURL:saveUrl
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) {
if (success) {
NSLog(@"save OK");
[self reloadFiles];
[self performSegueWithIdentifier:@"masterToDetail"
sender:self];
} else {
NSLog(@"failed to save!");
}
}];
}
} - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if (sender == self) {
// if sender == self, a new document has just been created,
// and chosenDocument is already set. UIViewController * destination = segue.destinationViewController;
if ([destination respondsToSelector:@selector(setDetailItem:)]) {
[destination setValue:self.chosenDocument forKey:@"detailItem"];
}
} else {
// find the chosen document from the tableview
NSIndexPath * indexPath = [self.tableView indexPathForSelectedRow];
NSString * filename = self.documentFilenames[indexPath.row];
NSURL * docUrl = [self urlForFilename:filename];
self.chosenDocument = [[BIDTinyPixDocument alloc] initWithFileURL:docUrl];
[self.chosenDocument openWithCompletionHandler:^(BOOL success) {
if (success) {
NSLog(@"load OK");
UIViewController *destination = segue.destinationViewController;
if ([destination respondsToSelector:@selector(setDetailItem:)]) {
[destination setValue:self.chosenDocument forKey:@"detailItem"];
}
} else {
NSLog(@"failed to load!");
}
}];
}
}
3、创建BIDTinyPixView视图类,用于显示用户可编辑的网格。
#import <UIKit/UIKit.h>
@class BIDTinyPixDocument; @interface BIDTinyPixView : UIView
@property (strong, nonatomic) BIDTinyPixDocument * document;
@end
#import "BIDTinyPixView.h"
#import "BIDTinyPixDocument.h" typedef struct {
NSUInteger row;
NSUInteger column;
} GridIndex; @interface BIDTinyPixView () @property (assign, nonatomic) CGSize blockSize;
@property (assign, nonatomic) CGSize gapSize;
@property (assign, nonatomic) GridIndex selectedBlockIndex; @end @implementation BIDTinyPixView - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self commonInit];
}
return self;
} - (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self commonInit];
}
return self;
} - (void)commonInit{
_blockSize = CGSizeMake(, );
_gapSize = CGSizeMake(, );
_selectedBlockIndex.row = NSNotFound;
_selectedBlockIndex.column = NSNotFound;
} // Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
if (!_document) return; for (NSUInteger row = ; row < ; row++) {
for (NSUInteger column = ; column < ; column++) {
[self drawBlockAtRow:row column:column];
}
}
} - (void)drawBlockAtRow:(NSUInteger)row column:(NSUInteger)column {
CGFloat startX = (_blockSize.width + _gapSize.width) * ( - column) + ;
CGFloat startY = (_blockSize.height + _gapSize.height) * row + ;
CGRect blockFrame = CGRectMake(startX, startY, _blockSize.width, _blockSize.height);
UIColor *color = [_document stateAtRow:row column:column] ?
[UIColor blackColor] : [UIColor whiteColor];
[color setFill];
[self.tintColor setStroke];
UIBezierPath *path = [UIBezierPath bezierPathWithRect:blockFrame];
[path fill];
[path stroke];
} - (GridIndex)touchedGridIndexFromTouches:(NSSet *)touches {
GridIndex result;
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
result.column = - (location.x * 8.0 / self.bounds.size.width);
result.row = location.y * 8.0 / self.bounds.size.height;
return result;
} - (void)toggleSelectedBlock {
[_document toggleStateAtRow:_selectedBlockIndex.row
column:_selectedBlockIndex.column];
[[_document.undoManager prepareWithInvocationTarget:_document]
toggleStateAtRow:_selectedBlockIndex.row column:_selectedBlockIndex.column];
[self setNeedsDisplay];
} - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.selectedBlockIndex = [self touchedGridIndexFromTouches:touches];
[self toggleSelectedBlock];
} - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
GridIndex touched = [self touchedGridIndexFromTouches:touches];
if (touched.row != _selectedBlockIndex.row
|| touched.column != _selectedBlockIndex.column) {
_selectedBlockIndex = touched;
[self toggleSelectedBlock];
}
}
4、添加BIDDetailViewController内容
@interface BIDDetailViewController : UIViewController @property (strong, nonatomic) id detailItem; @end
#import "BIDDetailViewController.h"
#import "BIDTinyPixView.h" @interface BIDDetailViewController ()
@property (weak, nonatomic) IBOutlet BIDTinyPixView *pixView;
- (void)configureView;
@end @implementation BIDDetailViewController #pragma mark - Managing the detail item - (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem; // Update the view.
[self configureView];
}
} - (void)configureView
{
// Update the user interface for the detail item. if (self.detailItem) {
self.pixView.document = self.detailItem;
[self.pixView setNeedsDisplay];
}
} - (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
}
//当用户按下返回按钮回到主列表时,文档实例将在没有进行任何保存操作的情况下被销毁,所以添加如下方法。
//一旦用户离开详情页面,就关闭文档,关闭文档会导致该文档被自动保存,保存工作发生在后台线程中。
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
UIDocument * doc = self.detailItem;
[doc closeWithCompletionHandler:nil];
}
5、添加iCloud支持
创建授权文件
6、如何查询
@property (strong, nonatomic) NSMetadataQuery * query;
@property (strong, nonatomic) NSMutableArray * documentURLs; - (void)reloadFiles {
NSFileManager * fileManager = [NSFileManager defaultManager];
// passing nil is OK here, matches first entitlement
NSURL * cloudURL = [fileManager URLForUbiquityContainerIdentifier:nil];
NSLog(@"got cloudURL %@", cloudURL); // returns nil in simulator self.query = [[NSMetadataQuery alloc] init];
_query.predicate = [NSPredicate predicateWithFormat:@"%K like '*.tinypix'",
NSMetadataItemFSNameKey];
_query.searchScopes = [NSArray arrayWithObject:
NSMetadataQueryUbiquitousDocumentsScope];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(updateUbiquitousDocuments:)
name:NSMetadataQueryDidFinishGatheringNotification
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(updateUbiquitousDocuments:)
name:NSMetadataQueryDidUpdateNotification
object:nil];
[_query startQuery];
} //实现查询完成时的那些通知
//查询的结果包含在一个NSMetadataItem对象的列表,从中我们可以获取文件URL和创建日期等数据项,我们根据创建日期来排列这些项,然后获取所有的URL以供之后使用。
- (void)updateUbiquitousDocuments:(NSNotification *)notification {
self.documentURLs = [NSMutableArray array];
self.documentFilenames = [NSMutableArray array]; NSLog(@"updateUbiquitousDocuments, results = %@", self.query.results);
NSArray *results = [self.query.results sortedArrayUsingComparator:
^NSComparisonResult(id obj1, id obj2) {
NSMetadataItem *item1 = obj1;
NSMetadataItem *item2 = obj2;
return [[item2 valueForAttribute:NSMetadataItemFSCreationDateKey] compare:
[item1 valueForAttribute:NSMetadataItemFSCreationDateKey]];
}]; for (NSMetadataItem *item in results) {
NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
[self.documentURLs addObject:url];
[(NSMutableArray *)_documentFilenames addObject:[url lastPathComponent]];
} [self.tableView reloadData];
}
7、保存到哪里
- (NSURL *)urlForFilename:(NSString *)filename
{
// be sure to insert "Documents" into the path
NSURL * baseURL = [[NSFileManager defaultManager]
URLForUbiquityContainerIdentifier:nil];
NSURL * pathURL = [baseURL URLByAppendingPathComponent:@"Documents"];
NSURL * destinationURL = [pathURL URLByAppendingPathComponent:filename];
return destinationURL;
}
8、将首选项保存到iCloud
- (IBAction)chooseColor:(id)sender {
NSInteger selectedColorIndex = [(UISegmentedControl *)sender selectedSegmentIndex];
[self setTintColorForIndex:selectedColorIndex]; // NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// [prefs setInteger:selectedColorIndex forKey:@"selectedColorIndex"];
// [prefs synchronize]; NSUbiquitousKeyValueStore * prefs = [NSUbiquitousKeyValueStore defaultStore];
[prefs setLongLong:selectedColorIndex forKey:@"selectedColorIndex"];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated]; // NSUserDefaults * prefs = [NSUserDefaults standardUserDefaults];
// NSInteger selectedColorIndex = [prefs integerForKey:@"selectedColorIndex"]; NSUbiquitousKeyValueStore * prefs = [NSUbiquitousKeyValueStore defaultStore];
NSInteger selectedColorIndex = (int)[prefs longLongForKey:@"selectedColorIndex"];
[self setTintColorForIndex:selectedColorIndex];
[self.colorControl setSelectedSegmentIndex:selectedColorIndex];
}