是否有UIView调整大小事件?

liv*_*ove 98 iphone ipad ios

我有一个视图,其中包含imagerait的行和列.

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

此视图是另一个调整大小的视图的子视图.

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

Mic*_*hal 93

正如Uli在下面评论的那样,正确的方法是覆盖layoutSubviews和布局imageViews.

如果由于某种原因,你不能进行子类化和覆盖layoutSubviews,那么观察bounds应该可以工作,即使是有点脏.更糟糕的是,观察存在风险 - Apple不保证KVO可以在UIKit类上运行.在这里与Apple工程师一起阅读讨论:关联对象什么时候发布?

原始答案:

您可以使用键值观察:

[yourView addObserver:self forKeyPath:@"bounds" options:0 context:nil];
Run Code Online (Sandbox Code Playgroud)

并实施:

- (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:
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 实际上,布局子视图正是layoutSubviews的用途,因此观察尺寸变化虽然功能正常,但却是IMO糟糕的设计. (2认同)

Rud*_*vič 88

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

override var bounds: CGRect {
    didSet {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

如果没有子类化,使用智能键路径进行键值观察将会:

var boundsObservation: NSKeyValueObservation?

func beginObservingBounds() {
    boundsObservation = observe(\.bounds) { capturedSelf, _ in
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 不能保证检测“边界”或“框架”变化是否有效,这取决于将视图放置在视图层次结构中的位置。我会改写`layoutSubviews`。请参阅[this](/sf/answers/3563130371/)和[this](/sf/answers/350230751/)答案。 (4认同)
  • @Ali:这里已经提到了几次:`frame`是派生和运行时计算的属性.不要覆盖这一点,除非你有一个非常明智和明智的理由这样做.否则:使用`bounds`或(甚至更好)`layoutSubviews`. (3认同)
  • 这样一个创造圆圈的好方法.谢谢!`覆盖var bounds:CGRect {didSet {layer.cornerRadius = bounds.size.width/2}}` (3认同)
  • @Ali为什么这么认为? (2认同)

gwa*_*ang 30

创建UIView的子类,并覆盖layoutSubviews

  • 这样做的麻烦在于子视图不仅可以改变它们的大小,而且可以为该大小更改设置动画.当UIView运行动画时,它不会每次都调用layoutSubviews. (10认同)
  • @DavidJeske UIViewAnimationOptions 有一个名为 UIViewAnimationOptionLayoutSubviews 的选项。我想这也许有帮助。:) (2认同)

Adl*_*ler 7

相当古老但仍然是一个很好的问题.在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];
}
Run Code Online (Sandbox Code Playgroud)

覆盖setFrame:不是一个好主意.frame源自center,boundstransform,因此iOS的不一定会调用setFrame:.

  • 这是真的(理论上)。但是,在设置 frame 属性时(至少在 iOS 7.1 上)也不会调用 `setBounds:`。这可能是 Apple 为避免附加消息而添加的优化。 (2认同)
  • 事实上,“frame”和“bounds”都源自视图的底层“CALayer”;他们只是调用该层的吸气剂。`setFrame:` 设置图层的框架,而 `setBounds:` 设置图层边界。所以你不能只覆盖其中之一。此外,“layoutSubviews”被过度调用(不仅仅是在几何形状更改时),因此它也可能并不总是一个好的选择。还在寻找... (2认同)

War*_*ger 7

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()
    //...
}
Run Code Online (Sandbox Code Playgroud)


rek*_*kle 6

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

SETFRAME:(的CGRect)帧

方法.这是在更改视图的帧(即大小)时调用的方法.做这样的事情:

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

  // Do your custom code here.
}
Run Code Online (Sandbox Code Playgroud)

  • 永远不要覆盖`setFrame:`.`frame`是派生属性.看我的回答 (3认同)