ChatGPT解决这个技术问题 Extra ChatGPT

是否有 UIView 调整大小事件?

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

如果调整此视图的大小,我需要重新排列图像视图的位置。

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

有没有办法检测何时调整此视图的大小?

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

C
Community

正如 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 等等。
用于 UIViewControllers 的 viewWillTransition,而不是 UIView。
如果根据视图的当前大小需要添加/删除不同的子视图以及需要添加/删除不同的约束,是否仍然推荐使用 layoutSubviews 方法?
好吧,我想我只是为我的情况回答了这个问题。当我进行设置(取决于大小)时,我需要覆盖边界,它可以工作。当我在 layoutSubviews 中这样做时,它不会。
R
Rudolf Adamkovič

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

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

如果没有子类化,使用智能键路径的键值观察将可以:

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 答案。
g
gwang

创建 UIView 的子类,并覆盖 layoutSubviews


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

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 保持不变并抛出 =[ 观察者是迄今为止我得到的最干净的方法。和最好的!
r
rekle

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

setFrame:(CGRect)帧

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

- (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/…
此外,在我的 UITextView 子类中,由于自动旋转导致调整大小时,setFrame: 未被调用,而 layoutSubviews: 被调用。注意:我正在使用自动布局 & iOS 7.0。
从不覆盖 setFrame:frame 是派生属性。看我的回答
A
Adlai Holler

很老,但仍然是一个好问题。在 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:


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

如果您在 UIViewController 实例中,则覆盖 viewDidLayoutSubviews 可以解决问题。

override func viewDidLayoutSubviews() {
    // update subviews
}

需要更改 UIView 大小,而不是 UIViewController。
@GastónAntonioMontes 谢谢!您可能已经注意到 UIView 实例和 UIViewController 实例之间的关系。因此,如果您有一个没有附加 VC 的 UIView 实例,其他答案很好,但如果您碰巧附加到 VC,这就是您的人选。抱歉,它不适用于您的情况。