如何在iPhone / iPad中保留图形数据?

时间:2013-07-29 07:10:17

标签: ios graphics uiview persistence

我正在创建一个iPad应用程序,用户可以使用图像和文本创建图形内容。我将它存储在自定义UIView子类数组的内存中。这些视图子类中的每一个都可以包含任意数量的包含图像或文本的子视图。

现在我需要将它们保存在设备中。在我探索时,似乎有很多方法可以做到这一点,并想知道这种情况最好的方法。

4 个答案:

答案 0 :(得分:2)

看起来你要求最终成为Drawing应用程序的架构设计。这意味着最佳它实际上取决于您的特定用例,除非您提供非常详细的要求列表,否则无法完全回答。

但总的来说,我可以尝试给你一些一般的提示,无论如何都要与你自己的具体细节实现相结合。

此描述将针对此类应用可能需要的基本用例做出一些假设:

  • 用户可以使用多种工具创建图像以获得结果。这些可以是任何东西,从画笔到文本字段等等。
  • 可以保存有关 工具用于创建图片的信息以及 此工具如何影响图片当前外观的信息,以便允许用户稍后编辑图片

说这个,主要问题是:如何存储您的绘图状态以便以后恢复

确实有很多方法可以实现它,但我相信其中有两种方法可以被认为是#34;干净且足够有名"。

的NSKeyedArchiver

这不是我的最爱(难以维护),但如果你必须处理UIView,它可能会是最快的。 NSKeyedArchiver

  

.. NSCoder的具体子类,提供了一种编码对象的方法   (和标量值)成为独立于架构的格式,可以   存储在文件中。

它实现了Memento design patternPro Objective-C Design Patterns中描述的相同模式,顺便提一下,它提供了一个案例研究,其中包含许多与您匹配的最重要的用例: / p>

  
      
  • 绘图板允许用户的手指涂抹   [...]
  •   
  • 它允许用户保存涂鸦。
  •   
  • 允许用户打开已保存的涂鸦   [...]
  •   

它是一款带有绘图板的应用程序,您可以用手指画线。 你的看起来像是这个的简化版本,带有图像和文本而不是涂鸦。

那么,在这种特定情况下,使用 NSKeyedArchiver 的专业人士是什么? UIView已经实现了NSCoding protocol,即归档对象所需的archiving an UIImageView is really expensive。因此,对于您需要存储的大部分信息(坐标,框架大小,背景颜色......),除了...归档对象之外,您不必做任何事情。

对于UIView之上的任何其他属性(例如:图像的本地路径,因为this article),您可以查看,其中详细说明了您拥有的内容要做的是利用 NSKeyedArchiver 来存储你的对象状态。

这一切归结为:

  1. 为您的绘图应用程序提供的每个工具实施NSCoding协议
  2. 跟踪用户创建的子视图(图片,文字......)
  3. 当用户点击" save"时,循环浏览它们,创建一个存档,并将它们存储到一个感性的路径中。路径的第一个组件可以是Drawing的名称,第二个组件是工具的名称,第三个组件是每次使用工具时的ID。像

    // A mountain image
    /<path to you Document dir>/Mountains/Image/1
    
    // A sun
    /<path to you Document dir>/Mountains/Image/2
    
    // The text "Mountain is awesome"
    /<path to you Document dir>/Mountains/Text/1  
    
  4. 当然,你必须在plist文件或 NSUserDefault 中保存绘图名称列表,以便能够向用户显示它们以防万一他们想要恢复它们进行编辑。

  5. Core data

    Mix of the two aforementioned techniques

    这可能是存储对象状态的最简洁,更易于维护的方式,但是会有点困难和麻烦,特别是如果它是第一次使用核心数据时。我不打算深入研究核心数据,但我可以给你一些整个程序的指导。基本上:

    1. 您创建一个数据库架构,代表您将让用户使用的每个工具。例如:图像的表格,文本的表格等等
    2. 在每张桌子上放置您需要记住的属性(位置,文字颜色为&#34;文字&#34;图片网址为&#34;图片&#34;依此类推)
    3. 您为用户创建的 Drawing 创建一个表,与工具表有1-many关系。该关系表示图中所示的对象。
    4. 根据db
    5. 中存储的内容初始化绘制画布和每个组件
    6. 每次用户点击&#34;保存&#34;时,创建或更新正确的数据库表,以反映存储中的当前图纸配置。
    7. 此方法的一个优点是,如果有一天您想要更改工具组件属性或添加新属性,则可以利用模式迁移以向后提供与新更新的兼容性。因此用户仍然可以使用他们的旧图纸。

      依旧等等......

      这是两种可能性。您也可以使用:

      • NSUSerDefault 用于存储状态,我建议避免使用。这将是非常难以维持的
      • this
      • 如果您计划仅提供&gt; = iOS6支持,则可以查看{{3}}

      我所描述的两个正是我认为通常和最常讨论的方式。你可以在书本,教程中找到它们,它们可以为你所做的任何事情提供很大的灵活性 如果您需要更多解释性链接,请与我们联系。

答案 1 :(得分:1)

正如我在评论中提到的,您可能想要了解iOS的状态保存API。但是,如果你想构建自己的系统来实现这一点,使用一些聪明的类别和词典会非常简单。然后,您可以使用NSKeyedArchiver和NSKeyedUnarchiver序列化/反序列化您的词典。

例如:

@interface UIButton (MyAppCategory)

- (NSDictionary *)viewProperties;
- (void)configureFromProperties: (NSDictionary *) properties;

@end

@implementation UIButton (MyAppCategory)

- (NSDictionary *)viewProperties {
    return @{ @"class" : NSStringFromClass([self class]),
              @"frame" : [NSValue valueWithRect:self.frame],
              @"titleLabelText" : self.titleLabel.text,
              // etc...
            };
}

- (void)configureFromProperties: (NSDictionary *) properties {
    NSValue * value = properties[@"frame"];
    if ([value isKindOfClass:[NSValue class]]) {
        self.frame = value.rectValue;
    }
    NSSString * titleLabelText = properties[@"titleLabelText"];
    if ([titleLabelText isKindOfClass:[NSString class]]) {
        self.titleLabel.text = titleLabelText;
    }
}

@end

// replicate the above pattern for other view objects you need to support


@implementation MyViewFactory

- (UIView)recreateViewFromProperties: (NSDictionary *) properties {
    NSString * className = properties[@"class"];
    if ([className isKindOfClass:[NSString class]]) {
        Class viewClass = NSClassFromString(className);
        id viewObject = [[viewClass alloc] init];
        if ([viewObject respondsToSelector:@selector(configureFromProperties:)]]) {
            [viewObject performSelector:@selector(configureFromProperties:) withObject:properties];
            return viewObject;
        }
    }
    return nil;
}

// exercise for the reader: iterate your views and use the viewProperties: method to collect your views' configuration info...

@end

答案 2 :(得分:1)

如果您想允许将来的会话编辑和加载等,我建议设计一个数据结构并从中创建核心数据模型。

保存会话元数据的一些结构,例如sessionID,creationDate,key of dictionary:imageName value:imageFrame(CGRect包装在NSValue中,使用setObjectForKey)。

为会话加载图像可以通过使用例如[sessionImageDictionary allKeys]将密钥调用到数组中,迭代遍历密钥并且异步地(NSOperationQueue with maxConcurrentOperationCount)将图像加载到某个宏路径来实现例如。库目录,并附加密钥,即imageName。

在同一次迭代中,您可以通过调用[sessionImageDictionary valueForKey:[arrayOfKeys objectAtIndex:currentIteration];将其先前存储的NSValue转换回CGRect来设置其帧。

数据结构全部取决于您想要的功能数量,但好处是它允许扩展并且核心数据作为后备存储,您可以执行诸如设备之间的同步,启用多个会话以进行加载和保存等一个“我的项目”功能。如果让用户建立一个图像库(都存储在你的应用程序库目录中),然后用户在同一个会话或多个会话中使用相同的图像,只需要存在一个图像副本,没有重复写入磁盘,核心数据对象将存储在会话中的文件名。

最重要的部分是构建正确的Core-Data模型并编写可以接受这些自定义子类的提取器,去除数据以创建,填充并将NSManagedObject保存到持久性存储。

答案 3 :(得分:1)

您最好的选择是将UIDocumentNSFileWrapper文件夹一起使用。然后,您可以将所有文件存储在一个文件夹中,该文件夹会在内容更改时自动保存。 请参阅:http://developer.apple.com/library/ios/#documentation/DataManagement/Conceptual/DocumentBasedAppPGiOS/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011149-CH1-SW1