【问题标题】:Is there a UIView resize event?是否有 UIView 调整大小事件?
【发布时间】:2011-04-29 09:03:14
【问题描述】:

我有一个视图,其中包含图像视图的行和列。

如果调整了这个视图的大小,我需要重新排列 imageviews 的位置。

这个视图是另一个被调整大小的视图的子视图。

有没有办法检测这个视图何时被调整大小?

【问题讨论】:

  • 视图何时调整大小?设备旋转时,还是用户使用多点触控旋转设备时?
  • 当用户点击另一个视图(主视图)上的按钮时,此视图会调整大小。

标签: iphone ipad ios


【解决方案1】:

正如 Uli 在下面评论的那样,正确的做法是覆盖 layoutSubviews 并在那里布局 imageViews。

如果由于某种原因,您不能继承和覆盖layoutSubviews,观察bounds 应该可以工作,即使有点脏。更糟糕的是,观察存在风险——Apple 不保证 KVO 可以在 UIKit 类上工作。在此处阅读与 Apple 工程师的讨论:When does an associated object get released?

原答案:

您可以使用键值观察:

[yourView addObserver:self forKeyPath:@"bounds" options:0 context:nil];

并实施:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (object == yourView && [keyPath isEqualToString:@"bounds"]) {
        // do your stuff, or better schedule to run later using performSelector:withObject:afterDuration:
    }
}

【讨论】:

  • 真的,布局子视图正是 layoutSubviews 的用途,所以观察尺寸变化,虽然功能,是 IMO 糟糕的设计。
  • @uliwitness 他们确实一直在改变这些东西。无论如何,现在你应该使用viewWillTransition 等等等等。
  • viewWillTransition 用于 UIViewControllers,而不是 UIView。
  • 如果根据当前视图的大小需要添加/删除不同的子视图以及需要添加/删除不同的约束,是否仍然推荐使用layoutSubviews
  • 好吧,我想我只是为我的情况回答了这个问题。当我进行设置(取决于大小)时,我需要覆盖边界,它可以工作。当我在 layoutSubviews 中这样做时,它不会。
【解决方案2】:

UIView 子类中,可以使用属性观察者

override var bounds: CGRect {
    didSet {
        // ...
    }
}

如果没有子类化,key-value 观察smart key-paths 就可以了:

var boundsObservation: NSKeyValueObservation?

func beginObservingBounds() {
    boundsObservation = observe(\.bounds) { capturedSelf, _ in
        // ...
    }
}

【讨论】:

  • @Ali 你为什么这么认为?
  • 加载帧发生变化,但看起来边界没有(我知道这很奇怪,也许我错了)
  • @Ali:正如这里多次提到的那样:frame 是派生和运行时计算的属性。不要覆盖这个,除非你有一个非常聪明和知道的理由这样做。否则:使用bounds 或(甚至更好)layoutSubviews
  • 创建一个圈子的好方法。谢谢! override var bounds: CGRect { didSet { layer.cornerRadius = bounds.size.width / 2 }}
  • 检测boundsframe 更改不能保证有效,具体取决于您将视图放在视图层次结构中的位置。我会改写layoutSubviews。请参阅thisthis 的答案。
【解决方案3】:

创建 UIView 的子类,并覆盖 layoutSubviews

【讨论】:

  • 这样做的问题是子视图不仅可以改变它们的大小,而且它们可以动画化改变大小。 UIView在运行动画时,不会每次都调用layoutSubviews。
  • @DavidJeske UIViewAnimationOptions 有一个名为 UIViewAnimationOptionLayoutSubviews 的选项。我认为这可能会有所帮助。 :)
【解决方案4】:

Swift 4 keypath KVO -- 这就是我检测自动旋转并移动到 iPad 侧面板的方式。应该工作任何视图。必须观察 UIView 的层。

private var observer: NSKeyValueObservation?

override func viewDidLoad() {
    super.viewDidLoad()
    observer = view.layer.observe(\.bounds) { object, _ in
        print(object.bounds)
    }
    // ...
}
override func viewWillDisappear(_ animated: Bool) {
    observer?.invalidate()
    //...
}

【讨论】:

  • 这个解决方案对我有用。我是 Swift 新手,我不确定您在此处使用的 \.bounds 语法。这到底是什么意思?
  • .layer 成功了!你知道为什么使用view.observe 不起作用吗?
  • @d4Rk - 我不知道。我的猜测是,层边界比 UIView 框架更不模糊。例如,UITableView 的 contentOffset 会影响其子视图的最终坐标。不确定。
  • 这对我来说是一个很好的答案。我的情况是我使用 AutoLayout 来布置我的视图。但是 UICollectionViewFlowLayout 会强制您给出明确的 itemSize。但是当您的 collectionView 大小发生变化时(例如在我显示带有 AutoLayout 的工具栏时)- itemSize 保持不变并抛出 =[ 观察者是迄今为止我得到的最干净的方法。最好的!
【解决方案5】:

您可以创建 UIView 的子类并覆盖

setFrame:(CGRect)frame

方法。这是当视图的框架(即大小)改变时调用的方法。做这样的事情:

- (void) setFrame:(CGRect)frame
{
  // Call the parent class to move the view
  [super setFrame:frame];

  // Do your custom code here.
}

【讨论】:

  • 明智且实用。好电话。
  • 仅供参考 这不适用于 UIImageView 子类。更多信息在这里:stackoverflow.com/questions/19216684/…
  • 另外,setFrame: 在我的UITextView 子类中没有被调用,而layoutSubviews: 是在自动旋转引起的。注意:我使用的是自动布局和 iOS 7.0。
  • 永远不要覆盖setFrame:frame 是派生属性。看我的回答
【解决方案6】:

相当老,但仍然是一个好问题。在 Apple 的示例代码和他们的一些私有 UIView 子类中,它们重写 setBounds 大致如下:

-(void)setBounds:(CGRect)newBounds {
    BOOL const isResize = !CGSizeEqualToSize(newBounds.size, self.bounds.size);
    if (isResize) [self prepareToResizeTo:newBounds.size]; // probably saves 
    [super setBounds:newBounds];
    if (isResize) [self recoverFromResizing];
}

覆盖setFrame: 不是一个好主意。 frame派生自centerboundstransform,所以iOS不一定会调用setFrame:

【讨论】:

  • 这是真的(理论上)。然而,setBounds: 在设置 frame 属性时也不会被调用(至少在 iOS 7.1 上)。这可能是 Apple 为避免额外消息而添加的优化。
  • ……苹果方面的糟糕设计是不调用自己的访问器。
  • 其实两者framebounds都是从视图底层的CALayer派生而来的;他们只是调用层的吸气剂。 setFrame: 设置图层的框架,而setBounds: 设置图层边界。因此,您不能只覆盖其中一个。此外,layoutSubviews 被过度调用(不仅仅是几何变化),因此它也可能并不总是一个好的选择。还在寻找...
【解决方案7】:

如果您在 UIViewController 实例中,覆盖 viewDidLayoutSubviews 就可以了。

override func viewDidLayoutSubviews() {
    // update subviews
}

【讨论】:

  • UIView 大小需要改变,而不是 UIViewController。
  • @GastónAntonioMontes 谢谢!您可能已经注意到UIView 实例和UIViewController 实例之间的关系。因此,如果您有一个没有附加 VC 的 UIView 实例,那么其他答案很好,但如果您碰巧附加到 VC,这就是您的人选。抱歉,它不适用于您的情况。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-09
  • 2013-07-16
  • 1970-01-01
相关资源
最近更新 更多