[cocoa-touch] UIView를 강제로 다시 그리는 가장 강력한 방법은 무엇입니까?

항목 목록이있는 UITableView가 있습니다. 항목을 선택하면 viewController가 푸시되고 다음을 수행합니다. viewDidLoad 메서드에서 내 하위 뷰 (drawRect가 재정의 된 UIView 하위 클래스)에 필요한 데이터에 대한 URLRequest를 실행합니다. 클라우드에서 데이터가 도착하면 뷰 계층 구조를 구축하기 시작합니다. 문제의 하위 클래스가 데이터를 전달 받고 이제 drawRect 메서드에 렌더링에 필요한 모든 것이 있습니다.

그러나.

나는 drawRect를 명시 적으로 호출하지 않기 때문에-Cocoa-Touch가 그것을 처리합니다.-저는 Cocoa-Touch에게 정말로이 UIView 서브 클래스가 렌더링하기를 원한다는 것을 알릴 방법이 없습니다. 언제? 이제 좋을 것입니다!

[myView setNeedsDisplay]를 시도했습니다. 이것은 가끔 작동합니다. 매우 반점이 있습니다.

나는 몇 시간 동안 이것과 씨름하고 있습니다. UIView를 다시 렌더링하도록 강요하는 견고하고 보장 된 접근 방식을 제발 제발 제발 제공 할 수 있습니까?

다음은 뷰에 데이터를 제공하는 코드 스 니펫입니다.

// Create the subview
self.chromosomeBlockView = [[[ChromosomeBlockView alloc] initWithFrame:frame] autorelease];

// Set some properties
self.chromosomeBlockView.sequenceString     = self.sequenceString;
self.chromosomeBlockView.nucleotideBases    = self.nucleotideLettersDictionary;

// Insert the view in the view hierarchy
[self.containerView          addSubview:self.chromosomeBlockView];
[self.containerView bringSubviewToFront:self.chromosomeBlockView];

// A vain attempt to convince Cocoa-Touch that this view is worthy of being displayed ;-)
[self.chromosomeBlockView setNeedsDisplay];

건배, 더그



답변

a UIView를 다시 렌더링 하도록하는 확실한 확실한 방법 은 [myView setNeedsDisplay]. 문제가있는 경우 다음 문제 중 하나가 발생할 수 있습니다.

  • 실제로 데이터를 갖기 전에 호출하거나 -drawRect:무언가를 오버 캐싱합니다.

  • 이 메서드를 호출하는 순간 뷰가 그려 질 것으로 예상합니다. Cocoa 그리기 시스템을 사용하여 의도적으로 “지금 당장이 순간에 그리기”를 요구할 방법이 없습니다. 이는 전체 뷰 합성 시스템, 쓰레기 성능을 방해하고 모든 종류의 아티팩트를 생성 할 수 있습니다. “다음 그리기주기에 그려야합니다.”라고 말하는 방법 만 있습니다.

필요한 것이 “일부 논리, 그리기, 더 많은 논리”인 경우 “일부 더 많은 논리”를 별도의 메서드에 넣고 다음을 사용하여 호출해야합니다. -performSelector:withObject:afterDelay: 넣고 지연 0으로 합니다. 그러면 “일부 논리”가 뒤에 추가됩니다. 다음 그리기주기. 이러한 종류의 코드의 예와 필요할 수있는 경우에 대해서는 이 질문 을 참조하십시오 (코드가 복잡해 지므로 가능하면 다른 솔루션을 찾는 것이 가장 좋습니다).

일이 그려지지 않는다고 생각되면 중단 점을 설정 -drawRect:하고 언제 전화가 오는지 확인하십시오. 을 호출 -setNeedsDisplay하고 있지만 -drawRect:다음 이벤트 루프에서 호출되지 않는 경우 뷰 계층 구조를 자세히 살펴보고 어딘가에 현명하지 않도록하십시오. 지나치게 영리함은 내 경험에서 나쁜 그림의 # 1 원인입니다. 시스템이 원하는 작업을 수행하도록 속이는 방법을 가장 잘 안다고 생각할 때 일반적으로 원하지 않는 작업을 정확하게 수행하게됩니다.


답변

setNeedsDisplay와 drawRect : (5 초) 호출 사이에 큰 지연이 발생했습니다. 메인 스레드가 아닌 다른 스레드에서 setNeedsDisplay를 호출했습니다. 이 호출을 주 스레드로 이동 한 후 지연이 사라졌습니다.

이것이 도움이되기를 바랍니다.


답변

환불이 보장되고 강화 된 콘크리트 견고한 방법으로 뷰를 동 기적으로 (호출 코드로 돌아 가기 전에) 그리 도록 강제하는 방법 은 CALayerUIView하위 클래스 와의 상호 작용 을 구성하는 것 입니다.

UIView 하위 클래스 displayNow()에서 레이어에“ set course for display ”다음“ make it so ” 를 지시 하는 메서드를 만듭니다 .

빠른

/// Redraws the view's contents immediately.
/// Serves the same purpose as the display method in GLKView.
public func displayNow()
{
    let layer = self.layer
    layer.setNeedsDisplay()
    layer.displayIfNeeded()
}

목표 -C

/// Redraws the view's contents immediately.
/// Serves the same purpose as the display method in GLKView.
- (void)displayNow
{
    CALayer *layer = self.layer;
    [layer setNeedsDisplay];
    [layer displayIfNeeded];
}

또한 draw(_: CALayer, in: CGContext)개인 / 내부 드로잉 메서드를 호출 할 메서드를 구현합니다 (every UIView이 a 이므로 작동 함 CALayerDelegate) .

빠른

/// Called by our CALayer when it wants us to draw
///     (in compliance with the CALayerDelegate protocol).
override func draw(_ layer: CALayer, in context: CGContext)
{
    UIGraphicsPushContext(context)
    internalDraw(self.bounds)
    UIGraphicsPopContext()
}

목표 -C

/// Called by our CALayer when it wants us to draw
///     (in compliance with the CALayerDelegate protocol).
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
    UIGraphicsPushContext(context);
    [self internalDrawWithRect:self.bounds];
    UIGraphicsPopContext();
}

그리고 internalDraw(_: CGRect)안전 장치와 함께 사용자 지정 방법을 만듭니다 draw(_: CGRect).

빠른

/// Internal drawing method; naming's up to you.
func internalDraw(_ rect: CGRect)
{
    // @FILLIN: Custom drawing code goes here.
    //  (Use `UIGraphicsGetCurrentContext()` where necessary.)
}

/// For compatibility, if something besides our display method asks for draw.
override func draw(_ rect: CGRect) {
    internalDraw(rect)
}

목표 -C

/// Internal drawing method; naming's up to you.
- (void)internalDrawWithRect:(CGRect)rect
{
    // @FILLIN: Custom drawing code goes here.
    //  (Use `UIGraphicsGetCurrentContext()` where necessary.)
}

/// For compatibility, if something besides our display method asks for draw.
- (void)drawRect:(CGRect)rect {
    [self internalDrawWithRect:rect];
}

그리고 이제 myView.displayNow()그릴 때 정말로 필요할 때마다 호출하십시오 (예 : CADisplayLink콜백에서) . 우리의 displayNow()메서드는 CALayerto 에게 알려줄 displayIfNeeded()것입니다. 그러면 동기식으로 다시 호출 draw(_:,in:)하여에서 그리기를 수행하여 internalDraw(_:)계속 진행하기 전에 컨텍스트에 그려진 내용으로 시각적 개체를 업데이트합니다.


이 접근 방식은 위의 @RobNapier와 유사하지만 displayIfNeeded()을 추가로 호출 하여 setNeedsDisplay()동기식으로 만드는 이점이 있습니다.

이것은 s가하는 CALayer것보다 더 많은 그리기 기능을 노출 하기 때문에 가능합니다. UIView레이어는 뷰보다 낮은 수준이고 레이아웃 내에서 고도로 구성 가능한 그리기를 위해 명시 적으로 설계되었으며 (Cocoa의 많은 것들과 마찬가지로) 유연하게 사용되도록 설계되었습니다. 부모 클래스, 위임자 또는 다른 드로잉 시스템에 대한 브리지 또는 자체적으로). CALayerDelegate프로토콜 의 적절한 사용은 이 모든 것을 가능하게합니다.

의 구성 가능성에 대한 자세한 내용은 Core Animation Programming GuideCALayerSetting Up Layer Objects 섹션 에서 찾을 수 있습니다 .


답변

나는 같은 문제가 있었고 SO 또는 Google의 모든 솔루션이 나를 위해 작동하지 않았습니다. 일반적으로 setNeedsDisplay작동하지만 작동하지 않을 때 … 가능한 모든 스레드와 항목에서 가능한 모든 방법으로 뷰를
호출하려고 시도했지만 setNeedsDisplay여전히 성공하지 못했습니다. Rob이 말했듯이

“다음 그리기주기에 그려야합니다.”

그러나 어떤 이유로 이번에는 그리지 않을 것입니다. 그리고 내가 찾은 유일한 해결책은 다음과 같이 무승부를 차단하는 모든 것이 사라지도록 잠시 후 수동으로 호출하는 것입니다.

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW,
                                        (int64_t)(0.005 * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
    [viewToRefresh setNeedsDisplay];
});

자주 다시 그리는 데 뷰가 필요하지 않은 경우 좋은 솔루션입니다. 그렇지 않으면 움직이는 (액션) 작업을 수행하는 경우 일반적으로을 호출하는 데 문제가 없습니다 setNeedsDisplay.

저처럼 그곳에서 길을 잃은 사람에게 도움이되기를 바랍니다.


답변

이것이 큰 변화이거나 프로젝트에 적합하지 않을 수도 있다는 것을 알고 있지만 이미 데이터를 확보 할 때까지 푸시를 수행하지 않는 것을 고려 했 습니까? 이렇게하면 뷰를 한 번만 그리면 사용자 경험도 향상됩니다. 푸시가 이미로드 된 상태로 이동합니다.

이를 수행하는 방법 UITableView didSelectRowAtIndexPath은 데이터를 비동기 적으로 요청하는 것입니다. 응답을 받으면 수동으로 segue를 수행하고 .NET의 viewController에 데이터를 전달합니다 prepareForSegue. 한편 간단한 로딩 표시기를 위해 활동 표시기를 표시하고 싶을 수 있습니다 https://github.com/jdg/MBProgressHUD


답변