ฉันสามารถใช้ CALayer เพื่อเพิ่มความเร็วในการเรนเดอร์มุมมองได้หรือไม่

ฉันกำลังสร้างออบเจ็กต์ NSView แบบกำหนดเองซึ่งมีเนื้อหาบางส่วนที่เปลี่ยนแปลงบ่อย และบางส่วนที่เปลี่ยนแปลงไม่บ่อยนัก ตามที่ปรากฎ ส่วนที่เปลี่ยนแปลงน้อยกว่าจะใช้เวลาในการวาดมากที่สุด สิ่งที่ฉันต้องการทำคือเรนเดอร์ทั้งสองส่วนนี้ในเลเยอร์ที่แตกต่างกัน เพื่อที่ฉันจะได้อัปเดตส่วนใดส่วนหนึ่งหรือส่วนอื่นแยกกัน ซึ่งจะทำให้ผู้ใช้ของฉันมีอินเทอร์เฟซผู้ใช้ที่เชื่องช้า

ฉันจะทำสิ่งนี้ได้อย่างไร? ฉันไม่พบบทช่วยสอนที่ดีเกี่ยวกับเรื่องประเภทนี้มากนัก และไม่มีใครพูดถึงการเรนเดอร์ NSBezierPaths บน CALayer ไอเดียใคร?


person mtmurdock    schedule 21.02.2012    source แหล่งที่มา


คำตอบ (1)


ลางสังหรณ์ของคุณถูกต้อง นี่เป็นวิธีที่ยอดเยี่ยมในการเพิ่มประสิทธิภาพการวาดภาพ ฉันทำเองโดยที่มีพื้นหลังแบบคงที่ขนาดใหญ่ซึ่งฉันต้องการหลีกเลี่ยงการวาดใหม่เมื่อองค์ประกอบต่างๆ ย้ายไปด้านบน

สิ่งที่คุณต้องทำคือเพิ่มออบเจ็กต์ CALayer สำหรับแต่ละรายการเนื้อหาในมุมมองของคุณ ในการวาดเลเยอร์ คุณควรตั้งค่ามุมมองของคุณเป็นตัวแทนสำหรับแต่ละเลเยอร์ จากนั้นจึงใช้เมธอด drawLayer:inContext:

ในวิธีการนั้น คุณเพียงแค่วาดเนื้อหาของแต่ละเลเยอร์:

- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)ctx
{
    if(layer == yourBackgroundLayer)
    {   
        //draw your background content in the context
        //you can either use Quartz drawing directly in the CGContextRef,
        //or if you want to use the Cocoa drawing objects you can do this:
        NSGraphicsContext* drawingContext = [NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:YES];
        NSGraphicsContext* previousContext = [NSGraphicsContext currentContext];
        [NSGraphicsContext setCurrentContext:drawingContext];
        [NSGraphicsContext saveGraphicsState];
        //draw some stuff with NSBezierPath etc
        [NSGraphicsContext restoreGraphicsState];
        [NSGraphicsContext setCurrentContext:previousContext];
    }
    else if (layer == someOtherLayer)
    {
        //draw other layer
    }
    //etc etc
}

เมื่อคุณต้องการอัพเดตเนื้อหาของเลเยอร์ใดเลเยอร์หนึ่ง เพียงโทร [yourLayer setNeedsDisplay] จากนั้นจะเรียกวิธีการมอบสิทธิ์ด้านบนเพื่อจัดเตรียมเนื้อหาที่อัปเดตของเลเยอร์

โปรดทราบว่าตามค่าเริ่มต้น เมื่อคุณเปลี่ยนเนื้อหาเลเยอร์ Core Animation จะให้การเปลี่ยนภาพจางลงที่ดีสำหรับเนื้อหาใหม่ อย่างไรก็ตาม หากคุณจัดการภาพวาดด้วยตัวเอง คุณอาจไม่ต้องการสิ่งนี้ ดังนั้นเพื่อป้องกันไม่ให้ภาพเคลื่อนไหวเริ่มต้นจางลงเมื่อเนื้อหาเลเยอร์เปลี่ยนแปลง คุณจะต้องใช้วิธีมอบหมาย actionForLayer:forKey: และป้องกันภาพเคลื่อนไหวด้วยการส่งคืน การกระทำที่เป็นโมฆะ:

- (id<CAAction>)actionForLayer:(CALayer*)layer forKey:(NSString*)key 
{
    if(layer == someLayer)
    {
        //we don't want to animate new content in and out
        if([key isEqualToString:@"contents"])
        {
            return (id<CAAction>)[NSNull null];
        }
    }
    //the default action for everything else
    return nil;
}
person Rob Keniger    schedule 21.02.2012
comment
ขอบคุณ สิ่งนี้มีประโยชน์มาก อย่างไรก็ตาม ฉันประสบปัญหาในการเริ่มวิธีการมอบหมายสิทธิ์ ฉันกำลังตั้งค่ามุมมองของฉันในฐานะผู้รับมอบสิทธิ์ และฉันกำลังเพิ่มเลเยอร์เป็นเลเยอร์ย่อยของเลเยอร์รูท และฉันกำลังเรียก setNeedsDisplay แต่ drawLayer:inContext ไม่เคยถูกเรียก มีความคิดอะไรบ้าง? - person mtmurdock; 21.02.2012
comment
ฉันกำลังแทนที่ drawRect: ด้วย นั่นอาจทำให้ไม่เรียกใช้ฟังก์ชันอื่นใช่หรือไม่ - person mtmurdock; 24.02.2012
comment
ใช่. คุณไม่สามารถมีทั้ง drawRect: และภาพวาดแบบเลเยอร์ในมุมมอง คุณต้องตรวจสอบให้แน่ใจว่าคุณได้เรียก setWantsLayer:YES ในมุมมองด้วย ไม่เช่นนั้นเลเยอร์จะถูกละเว้น - person Rob Keniger; 24.02.2012
comment
คุณจะโทรหา setWantsLayer: เมื่อไหร่? เพียงครั้งเดียวในตัวสร้าง? หรือทุกครั้งที่คุณวาด? - person mtmurdock; 24.02.2012
comment
โอเค ฉันได้แสดงมันแล้ว ฉันต้องกำหนดตำแหน่งและขอบเขตเพื่อให้มันปรากฏขึ้น แต่มันไม่ได้เรนเดอร์อย่างที่ฉันคาดหวัง ทุกอย่างหล่นไปจนสุดสายตาที่มุมขวาล่าง CALayers ใช้ระบบพิกัดที่แตกต่างจากมุมมองหรือไม่ ตำแหน่งสัมพันธ์กับมุมมองการเป็นเจ้าของหรือกับหน้าต่างหรือไม่? - person mtmurdock; 24.02.2012
comment
หากมุมมองได้รับการตั้งค่าในตัวสร้างอินเทอร์เฟซ คุณสามารถเปิดใช้งานได้ในตัวตรวจสอบ Core Animation สำหรับมุมมองนั้น (ทำเครื่องหมายที่ช่องถัดจากมุมมองในส่วน Core Animation Layer) หรือเปิดใช้งานโดยใช้วิธี initWithFrame: (หากคุณกำลังสร้างเลเยอร์โดยทางโปรแกรม) หรือใน initWithCoder: หากคุณโหลดมุมมองจากปลายปากกา - person Rob Keniger; 24.02.2012
comment
ฉันไม่เข้าใจ. ฉันกำลังเปิดอะไร และมันทำอะไร? - person mtmurdock; 24.02.2012
comment
CALayers ใช้วิธีอื่นในการระบุพิกัด ดูที่ เอกสาร - person Rob Keniger; 24.02.2012
comment
ฉันหมายถึง setWantsLayer - person Rob Keniger; 24.02.2012