5

我正在使用平移、捏合和旋转 UIGestureRecognizers 来允许用户将某些 UI 元素准确地放在他们想要的位置。使用此处的代码http://www.raywenderlich.com/6567/uigesturerecognizer-tutorial-in-ios-5-pinches-pans-and-more(或此处的类似代码http://mobile.tutsplus.com/ tutorials/iphone/uigesturerecognizer/)都为我提供了用户根据需要放置这些 UI 元素所需的东西。

当用户退出“UI 布局”模式时,我保存 UIView 的变换和中心,如下所示:

NSString *transformString = NSStringFromCGAffineTransform(self.transform);
[[NSUserDefaults standardUserDefaults] setObject:transformString forKey:@"UItransform", suffix]];

NSString *centerString = NSStringFromCGPoint(self.center);
[[NSUserDefaults standardUserDefaults] setObject:centerString forKey:@"UIcenter"];

当我重新加载应用程序时,我读取了 UIView 的转换和中心,如下所示:

NSString *centerString = [[NSUserDefaults standardUserDefaults] objectForKey:@"UIcenter"];
if( centerString != nil )
    self.center = CGPointFromString(centerString);

NSString *transformString = [[NSUserDefaults standardUserDefaults] objectForKey:@"UItransform"];

if( transformString != nil )
    self.transform = CGAffineTransformFromString(transformString);

UIView 最终会正确旋转和缩放,但在错误的位置。此外,再次进入“UI 布局”模式后,我不能总是用各种手势抓取视图(好像显示的视图不是手势识别器所理解的视图?)

我还有一个重置按钮,可将 UIView 设置transform为身份,并将其center设置为从 NIB 加载时的任何内容。但是在加载改变后的 UIView 中心和变换后,即使重置也不起作用。UIView 的位置不对。

我的第一个想法是,由于这些手势代码示例center发生了变化,因此旋转必须围绕不同的中心进行(假设一些不可预测的移动、旋转和缩放序列)。由于我不想保存整个编辑序列(尽管如果我想在布局模式下拥有一些撤消功能,这可能会很方便),我更改了 UIPanGestureRecognizer 处理程序以使用转换来移动它。一旦我开始工作,我想只要保存转换就会让我得到当前的位置和方向,不管事情发生的顺序是什么。但没有这样的运气。我仍然以这种方式获得一个古怪的位置。

所以我很茫然。如果 UIView 已被移动并旋转到新位置,我该如何保存该位置和方向,以便稍后加载它并将 UIView 恢复到应有的位置?

如果我没有正确标记此内容或没有正确布局或犯了其他一些 stackoverflow 罪行,请提前道歉。这是我第一次在这里发帖。

编辑

到目前为止,我正在尝试这两个建议。我认为它们实际上是同一件事(一个建议保存框架,另一个建议保存原点,我认为是 frame.origin)。

所以现在首选项代码中的保存/加载包括以下内容。

节省:

NSString *originString = NSStringFromCGPoint(self.frame.origin);
[[NSUserDefaults standardUserDefaults] setObject:originString forKey:@"UIorigin"];

加载(加载转换之前):

NSString *originString = [[NSUserDefaults standardUserDefaults] objectForKey:@"UIorigin"];
if( originString ) {
    CGPoint origin = CGPointFromString(originString);
    self.frame = CGRectMake(origin.x, origin.y, self.frame.size.width, self.frame.size.height);
}

我得到了相同(或相似——很难说)的结果。事实上,我添加了一个按钮来重新加载首选项,一旦视图旋转,“重新加载”按钮将重复移动 UIView 一些偏移量(好像框架或变换是相对于自身的 - 我敢肯定是一个线索,但我不确定它指的是什么)。

编辑#2

这让我想知道取决于视图的frame. 来自 Apple http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/WindowsandViews/WindowsandViews.html#//apple_ref/doc/uid/TP40009503-CH2-SW6(强调我的):

center 属性中的值始终有效,即使缩放或旋转因子已添加到视图的变换中。frame 属性中的值也不是这样,如果视图的变换不等于恒等变换,则认为该值无效。

编辑#3

好的,所以当我加载首选项时,一切看起来都很好。UI 面板的bounds矩形是 {{0, 0}, {506, 254}}。在我的 VC 的 viewDidLoad 方法结束时,一切似乎都还可以。但是当事情真正被展示出来时,bounds就是另外一回事了。例如:{{0, 0}, {488.321, 435.981}}(看起来就像旋转和缩放后它在其父视图中的大小)。如果我将界限重新设置为应有的界限,它就会移回原位。

将界限重置为他们应该以编程方式设置的界限很容易,但我实际上不确定何时执行!我本来想在 viewDidLoad 结束时这样做,但bounds那时仍然是正确的。

编辑#4

我尝试在 initWithCoder 中捕获 self.bounds(因为它来自 NIB),然后在 layoutSubviews 中,将 self.bounds 重置为捕获的 CGRect。那行得通。

但它看起来非常骇人听闻并且充满了危险。这真的不是正确的方法。(可以吗?) skram 下面的回答似乎很简单,但在应用程序重新加载时对我不起作用。

4

5 回答 5

2

您也将保存该frame属性。您可以使用NSStringFromCGRect()CGRectFromString()

加载时,设置框架然后应用您的变​​换。这就是我在我的一个应用程序中执行此操作的方式。

希望这可以帮助。

更新:就我而言,我有 DraggableUIViews可以应用旋转和调整大小。我NSCoding用来保存和加载我的对象,示例如下。

//encoding
....
[coder encodeCGRect:self.frame forKey:@"rect"];
// you can save with NSStringFromCGRect(self.frame);
[coder encodeObject:NSStringFromCGAffineTransform(self.transform) forKey:@"savedTransform"];

//init-coder
CGRect frame = [coder decodeCGRectForKey:@"rect"];
// you can use frame = CGRectFromString(/*load string*/);
[self setFrame:frame];
self.transform = CGAffineTransformFromString([coder decodeObjectForKey:@"savedTransform"]);

这样做是保存我的框架和变换,并在需要时加载它们。NSStringFromCGRect()和可以应用相同的方法CGRectFromString()

更新2:在你的情况下。你会做这样的事情..

[self setFrame:CGRectFromString([[NSUserDefaults standardUserDefaults] valueForKey:@"UIFrame"])];
self.transform = CGAffineTransformFromString([[NSUserDefaults standardUserDefaults] valueForKey:@"transform"]);

假设您NSUserDefaults使用UIFrame, 和transform键保存。

于 2012-06-13T05:05:09.233 回答
2

我无法重现您的问题。我使用了以下代码,它执行以下操作:

  • 添加视图
  • 通过改变中心来移动它
  • 用变换缩放它
  • 用另一个变换旋转它,连接到第一个
  • 将变换和中心保存到字符串
  • 添加另一个视图并从字符串应用中心和变换

这会导致两个视图在完全相同的位置和位置:

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    view1.layer.borderWidth = 5.0;
    view1.layer.borderColor = [UIColor blackColor].CGColor;
    [self.view addSubview:view1];
    view1.center = CGPointMake(150,150);
    view1.transform = CGAffineTransformMakeScale(1.3, 1.3);
    view1.transform = CGAffineTransformRotate(view1.transform, 0.5);

    NSString *savedCentre = NSStringFromCGPoint(view1.center);
    NSString *savedTransform = NSStringFromCGAffineTransform(view1.transform);

    UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    view2.layer.borderWidth = 2.0;
    view2.layer.borderColor = [UIColor greenColor].CGColor;
    [self.view addSubview:view2];
    view2.center = CGPointFromString(savedCentre);
    view2.transform = CGAffineTransformFromString(savedTransform);

}

给予:

在此处输入图像描述

这与我对文档的期望有关,因为所有转换都发生在中心点周围,因此永远不会受到影响。我可以想象您无法将项目恢复到以前状态的唯一方法是,如果超级视图以某种方式不同,要么具有自己的变换或不同的框架,要么完全不同的视图。但我不能从你的问题中看出这一点。

总之,您问题中的原始代码应该可以工作,所以还有其他事情发生!希望这个答案能帮助您缩小范围。

于 2012-06-13T18:33:54.623 回答
1

您还应该保存 UIView 的位置,

CGPoint position = CGPointMake(self.view.origin.x, self.view.origin.y)

NSString _position = NSStringFromCGPoint(position);

// Do the saving
于 2012-06-13T05:11:39.753 回答
0

我不确定正在发生的所有事情,但这里有一些可能会有所帮助的想法。

1- skram 的解决方案似乎是合理的,但它是bounds你要保存的,而不是frame. (请注意,如果没有旋转,则中心和边界定义框架。因此,设置两者与设置框架相同。)从,您链接到的 IOS 查看编程指南:

重要如果视图的变换属性不是恒等变换,则该视图的框架属性的值是未定义的并且必须被忽略。将变换应用于视图时,您必须使用视图的边界和中心属性来获取视图的大小和位置。任何子视图的框架矩形仍然有效,因为它们是相对于视图边界的。

2-另一个想法。重新加载应用程序时,您可以尝试以下操作:

  • 首先,将视图的变换设置为恒等变换。
  • 然后,将视图的边界和中心设置为保存的值。
  • 最后,将视图的变换设置为保存的变换。

根据您的应用程序重新启动的位置,它可能会以一些旧几何图形重新启动。我真的不认为这会改变任何事情,但尝试起来很容易。
更新: 经过一些测试,看起来这确实没有任何效果。更改变换似乎不会更改边界或中心(尽管它确实会更改框架。)

3-最后,您可以通过重写捏合手势识别器来操作bounds而不是transform. (再次使用bounds,而不是frame,因为较早的旋转可能会使frame无效。)这样,变换仅用于旋转,我认为,如果不重绘​​,就不能以任何其他方式完成。

在同一指南中,Apple 的建议是:

当您想要实现动画时,通常会修改视图的变换属性。例如,您可以使用此属性创建视图围绕其中心点旋转的动画。您不会使用此属性对视图进行永久性更改,例如在其父视图的坐标空间内修改其位置或视图大小。对于这种类型的更改,您应该修改视图的框架矩形。

于 2012-06-13T10:06:36.973 回答
0

感谢所有提供答案的人!它们的总和使我得出以下结论:

问题似乎是boundsCGRect 在启动时从首选项加载转换后被重置,但在实时修改时更新首选项时却没有。

我认为有两种解决方案。一种是首先加载首选项 fromlayoutSubviews而不是 from viewDidLoad。调用bounds之后似乎没有任何事情发生。layoutSubviews

但是,由于我的应用程序中的其他原因,从视图控制器的viewDidLoad. 所以我使用的解决方案是这样的:

// UserTransformableView.h
@interface UserTransformableView : UIView {
    CGRect defaultBounds;
}

// UserTransformableView.m
- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if( self ) {
        defaultBounds = self.bounds;
    }
    return self;
}

- (void)layoutSubviews {
    self.bounds = defaultBounds;
}
于 2012-06-21T21:43:44.887 回答