주요 가치 관찰을 수행하고 UIView 프레임에서 KBO 콜백을 받으려면 어떻게 해야 합니까?
앞으로 어떤 변화가 있을지 지켜보고 싶습니다.UIView의frame,bounds또는center키 를 수행하려면 합니까?키-값 관찰을 사용하여 이를 달성하려면 어떻게 해야 합니까?
대개 KBO가 지원되지 않는 알림 또는 기타 관찰 가능한 이벤트가 있습니다.문서에 '아니오'라고 표시되더라도 표면적으로는 UIView를 지원하는 CALayer를 관찰하는 것이 안전합니다.CALayer를 관찰하는 것은 (아이바 조작 대신) KVO와 적절한 접근자를 광범위하게 사용하기 때문에 실제로 작동합니다.그것이 앞으로 잘 될 것이라는 보장은 없습니다.
어쨌든 보기 프레임은 다른 속성의 산물일 뿐입니다.따라서 다음 사항을 준수해야 합니다.
[self.view addObserver:self forKeyPath:@"frame" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"bounds" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"transform" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"position" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"zPosition" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"anchorPoint" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"anchorPointZ" options:0 context:NULL];
[self.view.layer addObserver:self forKeyPath:@"frame" options:0 context:NULL];
여기에서 전체 예를 참조하십시오. https://gist.github.com/hfossli/7234623
참고: 문서에서 지원된다고는 하지 않지만, 현재까지 모든 iOS 버전(현재 iOS 2 -> iOS 11)에서 작동합니다.
참고: 콜백이 최종 값에 도달하기 전에 여러 개의 콜백이 수신된다는 점에 유의하십시오.를 들어, 가 변경됩니다.position그리고.bounds(그 순서대로).
리액티브 코코아를 사용하면 할 수 있습니다.
RACSignal *signal = [RACSignal merge:@[
RACObserve(view, frame),
RACObserve(view, layer.bounds),
RACObserve(view, layer.transform),
RACObserve(view, layer.position),
RACObserve(view, layer.zPosition),
RACObserve(view, layer.anchorPoint),
RACObserve(view, layer.anchorPointZ),
RACObserve(view, layer.frame),
]];
[signal subscribeNext:^(id x) {
NSLog(@"View probably changed its geometry");
}];
그리고 언제인지만 인지.bounds 수 사항
@weakify(view);
RACSignal *boundsChanged = [[signal map:^id(id value) {
@strongify(view);
return [NSValue valueWithCGRect:view.bounds];
}] distinctUntilChanged];
[boundsChanged subscribeNext:^(id ignore) {
NSLog(@"View bounds changed its geometry");
}];
그리고 언제인지만 인지.frame 수 사항
@weakify(view);
RACSignal *frameChanged = [[signal map:^id(id value) {
@strongify(view);
return [NSValue valueWithCGRect:view.frame];
}] distinctUntilChanged];
[frameChanged subscribeNext:^(id ignore) {
NSLog(@"View frame changed its geometry");
}];
편집: 이 해결책이 충분히 철저하지 않다고 생각합니다.이 답변은 역사적인 이유로 보관됩니다.여기에서 제 최신 답변을 확인하십시오. https://stackoverflow.com/a/19687115/202451
프레임 속성에 대한 KVO를 수행해야 합니다.이 경우 "자체"는 UIViewController입니다.
관찰자 추가(일반적으로 viewDidLoad에서 수행):
[self addObserver:self forKeyPath:@"view.frame" options:NSKeyValueObservingOptionOld context:NULL];
관찰자 제거(일반적으로 할당 해제 또는 viewDidDisparen:
[self removeObserver:self forKeyPath:@"view.frame"];
변경에 대한 정보 가져오기
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if([keyPath isEqualToString:@"view.frame"]) {
CGRect oldFrame = CGRectNull;
CGRect newFrame = CGRectNull;
if([change objectForKey:@"old"] != [NSNull null]) {
oldFrame = [[change objectForKey:@"old"] CGRectValue];
}
if([object valueForKeyPath:keyPath] != [NSNull null]) {
newFrame = [[object valueForKeyPath:keyPath] CGRectValue];
}
}
}
현재는 KBO를 사용하여 뷰 프레임을 관찰할 수 없습니다.속성을 관찰하려면 KBO를 준수해야 합니다.안타깝게도 UIKit 프레임워크의 속성은 다른 시스템 프레임워크와 마찬가지로 일반적으로 관찰할 수 없습니다.
설명서에서 다음을 참조하십시오.
참고: UIKit 프레임워크의 클래스는 일반적으로 KVO를 지원하지 않지만 사용자 정의 보기를 포함하여 응용 프로그램의 사용자 정의 개체에 구현할 수 있습니다.
에는 NS OperationQueue와 몇 .operations재산이지만 그것들은 명시적으로 문서화되어야 합니다.
뷰의 속성에서 KBO를 사용하는 것이 현재 효과가 있을지라도 배송 코드에서는 사용하지 않는 것이 좋습니다.그것은 취약한 접근법이며 문서화되지 않은 행동에 의존합니다.
수 있다면: 했듯이, 제가대기수여있할다면: 다사들이지적이듯했람른화에,이▁if듯지했적▁as다ation:이▁conversute▁the▁to▁have▁i▁others▁might.frame키 값 자체가 관측 가능한 것으로 보장되지 않으며 또한CALayer속성을 표시합니다.
정의 대사자정생수있다습니할성의를 할 수 .UIView재의하하클래를 하는 하위 setFrame:그리고 그 영수증을 대리인에게 알립니다.을 합니다.autoresizingMask모든 것을 유연하게 볼 수 있도록 합니다.합니다(「 」 (완작전히)).CALayer백업(별로 중요하지 않음) 및 크기 변경을 감시할 뷰의 하위 뷰로 추가합니다.
이것은 우리가 처음 iOS 5를 코딩할 API로 지정했을 때 iOS 4 아래에서 성공적으로 작동했고 결과적으로 일시적인 에뮬레이션이 필요했습니다.viewDidLayoutSubviews(을 나타냄)layoutSubviews그것이 더 적절했지만, 요점을 이해할 수 있습니다.
앞서 언급했듯이, KBO가 작동하지 않고 사용자가 제어할 수 있는 자신의 뷰만 관찰하려는 경우 setFrame 또는 setBounds를 재정의하는 사용자 정의 뷰를 만들 수 있습니다.주의할 점은 호출 시점에서 원하는 최종 프레임 값을 사용할 수 없다는 것입니다.따라서 값을 다시 확인하기 위해 다음 메인 스레드 루프에 GCD 호출을 추가했습니다.
-(void)setFrame:(CGRect)frame
{
NSLog(@"setFrame: %@", NSStringFromCGRect(frame));
[super setFrame:frame];
// final value is available in the next main thread cycle
__weak PositionLabel *ws = self;
dispatch_async(dispatch_get_main_queue(), ^(void) {
if (ws && ws.superview)
{
NSLog(@"setFrame2: %@", NSStringFromCGRect(ws.frame));
// do whatever you need to...
}
});
}
KVO 관찰에 의존하지 않고 다음과 같이 방법 스위즐을 수행할 수 있습니다.
@interface UIView(SetFrameNotification)
extern NSString * const UIViewDidChangeFrameNotification;
@end
@implementation UIView(SetFrameNotification)
#pragma mark - Method swizzling setFrame
static IMP originalSetFrameImp = NULL;
NSString * const UIViewDidChangeFrameNotification = @"UIViewDidChangeFrameNotification";
static void __UIViewSetFrame(id self, SEL _cmd, CGRect frame) {
((void(*)(id,SEL, CGRect))originalSetFrameImp)(self, _cmd, frame);
[[NSNotificationCenter defaultCenter] postNotificationName:UIViewDidChangeFrameNotification object:self];
}
+ (void)load {
[self swizzleSetFrameMethod];
}
+ (void)swizzleSetFrameMethod {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
IMP swizzleImp = (IMP)__UIViewSetFrame;
Method method = class_getInstanceMethod([UIView class],
@selector(setFrame:));
originalSetFrameImp = method_setImplementation(method, swizzleImp);
});
}
@end
이제 애플리케이션 코드에서 UIView에 대한 프레임 변경을 관찰합니다.
- (void)observeFrameChangeForView:(UIView *)view {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDidChangeFrameNotification:) name:UIViewDidChangeFrameNotification object:view];
}
- (void)viewDidChangeFrameNotification:(NSNotification *)notification {
UIView *v = (UIView *)notification.object;
NSLog(@"View '%@' did change frame to %@", v, NSStringFromCGRect(v.frame));
}
RxSwift 및 Swift 5에 대한 @hfossli 답변이 업데이트되었습니다.
RxSwift를 사용하면 할 수 있습니다.
Observable.of(rx.observe(CGRect.self, #keyPath(UIView.frame)),
rx.observe(CGRect.self, #keyPath(UIView.layer.bounds)),
rx.observe(CGRect.self, #keyPath(UIView.layer.transform)),
rx.observe(CGRect.self, #keyPath(UIView.layer.position)),
rx.observe(CGRect.self, #keyPath(UIView.layer.zPosition)),
rx.observe(CGRect.self, #keyPath(UIView.layer.anchorPoint)),
rx.observe(CGRect.self, #keyPath(UIView.layer.anchorPointZ)),
rx.observe(CGRect.self, #keyPath(UIView.layer.frame))
).merge().subscribe(onNext: { _ in
print("View probably changed its geometry")
}).disposed(by: rx.disposeBag)
그리고 언제인지만 인지.bounds 수 있는 사항
Observable.of(rx.observe(CGRect.self, #keyPath(UIView.layer.bounds))).subscribe(onNext: { _ in
print("View bounds changed its geometry")
}).disposed(by: rx.disposeBag)
그리고 언제인지만 인지.frame 수 있는 사항
Observable.of(rx.observe(CGRect.self, #keyPath(UIView.layer.frame)),
rx.observe(CGRect.self, #keyPath(UIView.frame))).merge().subscribe(onNext: { _ in
print("View frame changed its geometry")
}).disposed(by: rx.disposeBag)
KVO를 전혀 사용하지 않고 이를 달성할 수 있는 방법이 있으며, 다른 사람들이 이 게시물을 찾을 수 있도록 여기에 추가하겠습니다.
http://www.objc.io/issue-12/animating-custom-layer-properties.html
닉 록우드의 이 훌륭한 튜토리얼은 핵심 애니메이션 타이밍 기능을 사용하여 무엇이든 운전하는 방법을 설명합니다.내장된 타이밍 기능을 사용하거나 자신만의 큐빅 베지어 기능을 상당히 쉽게 만들 수 있기 때문에 타이머나 CAD 디스플레이 레이어를 사용하는 것보다 훨씬 우수합니다(첨부 기사(http://www.objc.io/issue-12/animations-explained.html) 참조).
다음과 같은 일부 UIKit 속성에서 KVO를 사용하는 것은 안전하지 않습니다.frame아니면 적어도 애플은 그렇게 말합니다.
반응형 코코아를 사용하는 것이 좋습니다. 이는 KBO를 사용하지 않고도 모든 속성의 변화를 청취하는 데 도움이 됩니다. 신호를 사용하여 무언가를 관찰하기 시작하는 것은 매우 쉽습니다.
[RACObserve(self, frame) subscribeNext:^(CGRect frame) {
//do whatever you want with the new frame
}];
언급URL : https://stackoverflow.com/questions/4874288/how-can-i-do-key-value-observing-and-get-a-kvo-callback-on-a-uiviews-frame
'programing' 카테고리의 다른 글
| 함수를 반환하는 함수를 만들려면 어떻게 해야 합니까? (0) | 2023.07.28 |
|---|---|
| MySQL에서 지정된 텍스트가 포함된 필드를 쿼리하려면 어떻게 해야 합니까? (0) | 2023.07.28 |
| Node.js - 생성자로 module.exports 사용 (0) | 2023.07.28 |
| 배열 목록을 방지합니다.인덱스 반환에서 추가() (0) | 2023.07.28 |
| 잘못된 iPhone 응용 프로그램 이진 (0) | 2023.07.28 |