แทนที่ UIViewController.view ด้วยประเภทเฉพาะ

ลองพิจารณาแอปพลิเคชันที่มีมุมมองที่ปรับแต่งเองสูงหรือซับซ้อน

เราจะมีวิธีการส่งตัวควบคุมมุมมองเฉพาะไปยัง UIView ประเภทใดประเภทหนึ่ง โดยที่ UIView นั้นประกอบด้วยมุมมองอื่นจำนวนหนึ่ง

มุมมองควรมีอินเทอร์เฟซเฉพาะโดเมนที่หลากหลาย ช่วยให้คอนโทรลเลอร์ทำงานได้โดยมีเลเยอร์ "กาว" บาง ๆ อยู่ระหว่างตัวควบคุมกับโมเดลที่มีเนื้อหาคล้ายกัน

ดังนั้นเราจึงแทนที่คุณสมบัติมุมมองของคอนโทรลเลอร์ของเราดังนี้:

@interface PlaybackViewController : UIViewController<StageLayoutDelegate, ControlPanelDelegate>
{
    NSMutableArray* _sections;
    LightingMode _lightingMode;
}

@property (nonatomic, strong) PlaybackView* view; // <------ Specific type of view

#pragma mark - injected
@property (nonatomic, strong) id<OscClient> oscClient;
@property (nonatomic, strong) AbstractStageLayoutView* stageLayoutView;

@end

Oververriding สมเหตุสมผลมากกว่าการกำหนดผู้เข้าถึงรายอื่น และฉันสามารถส่งข้อความไปยัง UIView ประเภทเฉพาะได้โดยไม่ต้องส่ง

ปัญหา: ปัญหาเดียวคือมันส่งผลให้เกิดคำเตือนคอมไพเลอร์:

ประเภทคุณสมบัติ 'PlaybackView *' เข้ากันไม่ได้กับประเภท 'UIView *' ที่สืบทอดมาจาก 'UIViewController'

. . และฉันชอบสร้างโค้ดที่ไม่มีคำเตือนใดๆ วิธีนี้จะทำให้คำเตือนที่ถูกต้องไม่พลาดโดยถูกฝังไว้ท่ามกลางคำเตือนอื่นๆ

คำถาม:

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

person Jasper Blues    schedule 05.02.2013    source แหล่งที่มา
comment
ทำไมคุณไม่สร้างคลาสย่อยของมุมมองและใช้มุมมอง VC หลักเป็นคอนเทนเนอร์ Apple แนะนำให้จัดองค์ประกอบของคลาสย่อยเสมอ   -  person Andrea    schedule 05.05.2014
comment
UITableViewController จัดการทำสิ่งที่คุณกำลังพูดถึงและผลก็คือไม่ยืดหยุ่นจนแทบจะไร้ประโยชน์ ฉันจะคิดหนักเกี่ยวกับแนวทางนี้ มันอาจจะกลับมากัดคุณในภายหลัง   -  person Kirby Todd    schedule 05.05.2014
comment
นี่เป็นวิธีที่รวดเร็วและสกปรกเพียงเพื่อระงับคำเตือน พยายามล้อมโค้ดของคุณระหว่างบรรทัด #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu" //YOUR CODE #pragma clang diagnostic pop หรือ -Wall   -  person Andrea    schedule 05.05.2014
comment
@JasperBlues ฉันได้แก้ไขคำตอบแล้ว แต่แน่นอนลองก่อน   -  person Andrea    schedule 05.05.2014
comment
@KirbyTodd ฉันยอมรับว่า UITableViewController มีการใช้งานที่ จำกัด แต่ฉันไม่คิดว่ามันเป็นเพราะมันเชื่อมโยงอย่างแน่นหนากับ UITableView . มีหลายกรณีที่ view-controller เชื่อมต่อกับมุมมองเฉพาะ . การทำเช่นนี้ช่วยให้ผู้ควบคุมรับบทบาทตามที่ตั้งใจไว้ในการเป็นชั้นกาวบางๆ ระหว่างมุมมองและแบบจำลองที่สมบูรณ์   -  person Jasper Blues    schedule 05.05.2014
comment
ฉันไม่สามารถทำซ้ำคำเตือนของคุณได้ คุณมีการตั้งค่าสถานะเพิ่มเติมหรือไม่ PlaybackView เป็นคลาสย่อย UIView หรือไม่   -  person Warren Burton    schedule 05.05.2014
comment
ฉันเดาว่าไม่ใช่เมื่อฉันทำให้ PlaybackView เป็นคลาสย่อย NSObject (แทนที่จะเป็น UIView) เมื่อฉันเห็นคำเตือน   -  person Warren Burton    schedule 05.05.2014
comment
@WarrenBurton นั่นจะสมเหตุสมผล แต่ฉันได้รับคำเตือนเมื่อ PlaybackView เป็น UIVIew ประเภทหนึ่ง ซึ่งยังคงเป็นไปตามสัญญาของซูเปอร์คลาส คุณไม่ได้รับคำเตือนสำหรับกรณีนี้ใช่ไหม   -  person Jasper Blues    schedule 05.05.2014
comment
เลขที่. ทันทีที่ PlaybackView เป็นคลาสย่อย UIView คำเตือนจะหายไป คุณใช้การประกาศ fwd เช่น @คลาสการเล่น View; หรือ #import PlaybackView.h ? ฉันใช้ #import   -  person Warren Burton    schedule 05.05.2014
comment
ใช่ ฉัน (กระตือรือร้นมากเกินไป) ใช้การประกาศล่วงหน้า สนใจที่จะให้คำตอบ? ฉันจะยอมรับมัน   -  person Jasper Blues    schedule 05.05.2014


คำตอบ (5)


ปัญหาที่นี่ไม่ใช่การแทนที่คุณสมบัติ แต่เป็นการใช้การประกาศล่วงหน้าของประเภทคลาส

ดังนั้นนี้...

@class PlaybackView;

@interface PlaybackViewController : UIViewController

@property (nonatomic, strong) PlaybackView* view;

@end

จะให้คำเตือนดังกล่าวแก่คุณเนื่องจากคอมไพเลอร์ไม่สามารถทราบลำดับชั้นการสืบทอดของ PlaybackView UIViewController มีสัญญาที่จะจัดหา UIView จากคุณสมบัติ view ของตน

มันบอกคุณว่ามันคิดว่า PlaybackView ไม่ใช่ UIView

วิธีแก้ปัญหาง่ายๆ ที่นี่คือการใช้ #import แทนเพื่อให้ความรู้แก่คอมไพเลอร์เกี่ยวกับ PlaybackView...

#import "PlaybackView.h"

@interface PlaybackViewController : UIViewController

@property (nonatomic, strong) PlaybackView* view;

@end

อีกทางหนึ่ง (แต่รูปแบบแย่มากเนื่องจาก PCH เป็นคุณสมบัติที่ปรับให้เหมาะสมและไม่ควรจัดการการพึ่งพา) คือการเพิ่ม #import "PlaybackView.h" ให้กับโครงการ PCH ของคุณ

person Warren Burton    schedule 05.05.2014
comment
แน่นอน! ชัดเจนมาก (ย้อนหลัง) . จะมอบรางวัล +100 ด้วยเมื่อหมดเวลา - person Jasper Blues; 05.05.2014

ตามที่แนะนำในคำตอบอื่นโดยใช้ #import แทน @class จะล้างคำเตือน แต่แนะนำให้นำเข้าน้อยที่สุดในส่วนหัว ดังนั้นฉันขอแนะนำให้ปล่อย view ไว้ไม่เปลี่ยนแปลงและมี PlaybackView * playbackView เพิ่มเติม:

  • เป็นการดีอย่างยิ่งที่จะให้ทั้ง view และ playbackView ชี้ไปที่มุมมองเดียวกัน
  • คลาสที่จำเป็นต้องมีความรู้เกี่ยวกับ view เฉพาะทางของคุณ จะต้องนำเข้าส่วนหัวของคอนโทรลเลอร์ของคุณ เพื่อให้พวกเขาสามารถใช้ playbackView ได้ตั้งแต่แรก
  • ที่สำคัญกว่านั้น หากคุณต้องการฝังมุมมองพิเศษของคุณเป็นมุมมองย่อยในอนาคต (ซึ่งมักเกิดขึ้นเช่นการเพิ่ม UIScrollView superview) คุณจะไม่ต้องปรับโครงสร้างโค้ดและคลาสอื่นอีก!
  • มันสะอาดกว่าชัดๆ
person Rivera    schedule 06.05.2014
comment
มันไม่สะอาดกว่าในความคิดของฉันอย่างแน่นอน ผู้อ่านโค้ดสร้างความสับสนหากมีลิงก์อื่นไปยังโครงสร้างที่คุ้นเคยมาก หากคุณจะเพิ่ม IBOutlet อื่น คุณจะต้องลองไปที่ IB เพื่อดูว่ามันเหมือนกับมุมมองจริงๆ หากคุณแทนที่มันอย่างชัดเจนใน ViewController คุณจะเห็นว่าแท้จริงแล้วมันคือ .view .view ฐานนั้นคืออะไร PlaybackView ฉันใช้เทคนิคที่ Warren Burton อธิบายไว้มาก เพราะฉันต้องการใช้มุมมองพื้นฐานนั้นสำหรับหน้าที่การดูแลทำความสะอาดมุมมองที่ไม่ได้อยู่ในรายการเฉพาะภายในมุมมองนั้น - person Lucas van Dongen; 13.11.2014

ฉันไม่คิดว่าการแทนที่คุณสมบัติมุมมอง UIViewControllers เป็นวิธีที่ดี

ฉันคิดว่าควรทำสิ่งนี้ดีกว่า:

@interface PlaybackViewController : UIViewController<StageLayoutDelegate, ControlPanelDelegate>
{
    NSMutableArray* _sections;
    LightingMode _lightingMode;
}

//@property (nonatomic, strong) PlaybackView* view; //you do not need this property

#pragma mark - injected
@property (nonatomic, strong) id<OscClient> oscClient;
@property (nonatomic, strong) AbstractStageLayoutView* stageLayoutView;

@end

และในไฟล์ .m

- (void)loadView
{
    PlaybackView *mainView = [[PlaybackView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
    // set the mainView
    self.view = mainView;
}

และคุณสามารถใช้ PlaybackView ของคุณได้เช่นนี้

((PlaybackView *)(self.view)).oscClient

or

((PlaybackView *)(xxxViewControler.view)).oscClient
person Guo Luchuan    schedule 05.02.2013
comment
@JasperBlues ในความคิดของฉัน มันมีความยืดหยุ่นมากกว่า เช่น navigationBar.leftItem เราไม่แทนที่ leftItem เราแค่ให้ leftItem เป็น buttonItem และหากมุมมองของคุณไม่จำเป็นต้องเป็นประเภท PlaybackView1 และบางครั้งก็เป็นประเภท PlaybackView2 เพียงเพิ่มบูลใน loadView ก็สามารถจัดการได้ และถ้าคุณแทนที่ ฉันคิดว่ามันไม่ง่ายที่จะทำ - person Guo Luchuan; 05.02.2013

บางทีคุณอาจประกาศวิธีการอื่นที่จัดเตรียมนักแสดงให้คุณได้

@implementation PlaybackViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    //  use view_ property instead of view
    self.view_.foo = 1;
}

- (void)loadView {
    CGRect frame = [UIScreen mainScreen].applicationFrame;
    self.view = [[PlaybackView alloc] initWithFrame:frame];
}

- (PlaybackView *)view_ {
    return (PlaybackView *)self.view;
}

ไม่ใช่วิธีที่สะอาดที่สุดอย่างแน่นอน แต่จะหลีกเลี่ยงการส่งใน self.view (แม้ว่าจะไม่ได้ใช้ self.view ก็ตาม)

person FluffulousChimp    schedule 05.05.2014
comment
นั่นก็เหมือนกับการจัดหาทรัพย์สินอื่นซึ่งเขาบอกว่าเขาไม่สนใจ - person Leo Natan; 05.05.2014
comment
ถ้าจะพูดเกินจริง OP บอกว่า ทรัพย์สิน ไม่ใช่ วิธีการ ฉันจะให้ไวยากรณ์ ObjC สมัยใหม่เบลอความแตกต่าง - person FluffulousChimp; 05.05.2014
comment
@NSBum ฉันคิดว่าฉันพูดว่า 'accessor' ซึ่งหมายถึงคุณสมบัติ (สังเคราะห์หรือวิธีการที่กำหนดเอง) แต่มันก็หมายถึงหมายถึง 'วิธีการ' เช่นกัน . . ยังไงก็ขอบคุณสำหรับคำแนะนำของ Leo Natan - person Jasper Blues; 05.05.2014
comment
ยุติธรรมเพียงพอ ฉันไม่สามารถคิดวิธีอื่นในการทำเช่นนี้ได้ แม้ว่าจะเป็นอาการระคายเคืองเรื้อรังก็ตาม - person FluffulousChimp; 05.05.2014

[อัปเดต]
ในที่สุดฉันก็พบวิธีแก้ปัญหาที่เหมาะกับปัญหาแล้ว นี่เป็นวิธีที่รวดเร็วและสกปรกเพียงเพื่อระงับคำเตือน พยายามล้อมโค้ดของคุณระหว่างบรรทัดเหล่านี้

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu" 
//YOUR CODE
#pragma clang diagnostic pop

หรือ -Wall หากต้องการดูเพิ่มเติมเกี่ยวกับการระงับคำเตือนคอมไพเลอร์ Clang Manual

[คำตอบเดิม]
ฉันต้องการให้ 2 เซนต์ของฉัน ถ้าฉันเข้าใจดีว่าคุณกำลังพยายามสร้างโรงงาน Abstract ซึ่งจะให้มุมมองเวอร์ชันพิเศษแก่คุณตามฟังก์ชันการทำงานของตัวควบคุมมุมมอง ในความคิดของฉันสตอรี่บอร์ดทำงานได้ไม่ดีนักในการออกแบบแบบนั้น แต่ฉันอยากจะให้วิสัยทัศน์ของฉันเกี่ยวกับเรื่องนี้แก่คุณ

ก่อนอื่น ฉันจะสร้างคลาสนามธรรมสำหรับตัวควบคุมมุมมองของคุณ โดยในอินเทอร์เฟซ คุณจะประกาศคุณสมบัติทั้งหมดที่คุณต้องการใน VC ย่อยทั้งหมดของคุณ เช่น:

  • OSClient
  • บทคัดย่อ StageLayoutView
  • PlaybackView มีระดับอ่อน
  • คุณสมบัติการเล่น

คลาส PlaybackView เป็นคลาสคลัสเตอร์เช่น NSNumber คุณเรียกใช้เมธอดแบบโรงงานซึ่งจะส่งคืนอ็อบเจ็กต์ที่อาจแตกต่างไปในแต่ละกรณี หากคุณตรวจสอบ NSnumber มันจะส่งคืนอ็อบเจ็กต์อื่นหากคุณสร้างทศนิยมหรือจำนวนเต็ม แต่เป็นคลาสย่อยทั้งหมดของ NSNumber และ NSNumber จะประกาศคุณสมบัติทั้งหมดของคลาสย่อย แต่ไม่ได้นำไปใช้งาน
ตอนนี้สิ่งที่คุณสามารถทำได้ในเมธอด -viewDidLoad ของคลาสนามธรรมคือการเรียกเมธอดแบบนั้น

PlaybackView *plbackView = [PlaybackView playbackViewFroProperty:self.playbackProperty];
[self.view addSubview:playbackView];
self.playbackView = plbackView;

PlayingProperty สามารถให้ค่าได้ภายใน User defined runtime attibute ในตัวแก้ไขสตอรี่บอร์ดของ viewcontroller

person Andrea    schedule 05.05.2014
comment
ไม่ ฉันขอโทษที่ฉันใช้คำพูดในคำถามไม่ดี ทั้งหมดที่ฉันพยายามทำคือระบุประเภทของ UIView ของฉันในคลาสย่อย UIViewController ของฉันให้เป็นเช่น PlaybackView* (ส่วนที่เป็นนามธรรมคือปลาเฮอริ่งแดง) . . หากคุณทำเช่นนี้ เสียงดังกราวจะเตือนโดยบังคับให้คุณร่ายไปทุกที่แทน สำหรับโค้ดที่กระชับ ฉันต้องการหลีกเลี่ยงการแคสต์ - person Jasper Blues; 05.05.2014
comment
ตอนนี้ฉันเข้าใจแล้ว แต่ฉันไม่มีคำตอบ :-( - person Andrea; 05.05.2014
comment
ใช่ ขออภัย คำถามของฉันไม่ชัดเจน . . ฉันได้เพิ่มคำชี้แจงเพิ่มเติมในคำถามเพื่อช่วยอธิบายสิ่งที่ฉันหมายถึง . คุณคิดอย่างไร? ดูเหมือนว่าจะต้องมีวิธีหนึ่ง แต่ฉันหามันไม่เจอ - person Jasper Blues; 05.05.2014
comment
คุณสามารถระงับคำเตือนของคอมไพเลอร์ที่ล้อมวิธีการของคุณไว้รอบๆ มาโครตัวประมวลผลล่วงหน้าสองตัว แต่คำเตือนก็มีเหตุผลในตัวเอง ฉันไม่รู้ว่ามันยอมรับสำหรับคุณหรือไม่ แต่ทำไมไม่สร้างวิธีการ myView แบบที่เรียกวิธีการดูแบบง่ายๆ - person Andrea; 05.05.2014
comment
ฉันมีข้อเสนอแนะสุดท้าย ฉันยอมแพ้ :) แล้ววิธีการแบบ Swizzling ที่รันไทม์คุณจะสลับวิธีการปลูกฝังด้วยวิธีอื่น ฉันไม่เคยใช้มัน แต่หากไม่ทราบคุณควรดู mikeash.com/pyblog/ - person Andrea; 05.05.2014
comment
การทดแทนมุมมองไม่ใช่ปัญหา ปัญหาคือคำเตือนของคอมไพเลอร์ น่าเสียดายที่การสวิงไม่ได้ช่วยอะไร - person FluffulousChimp; 05.05.2014
comment
@Andrea - การสวดภาวนาไม่ได้ช่วยอะไรที่นี่ เราต้องการให้คอนโทรลเลอร์ประเภทใดประเภทหนึ่งเชื่อมโยงกันอย่างแน่นหนากับมุมมองประเภทใดประเภทหนึ่ง เพื่อให้ผู้ควบคุมสามารถเรียกอินเทอร์เฟซเฉพาะของมุมมองนี้ได้โดยไม่ต้องแคสต์ . . สามารถทำได้อยู่แล้ว แต่เพื่อหลีกเลี่ยงคำเตือน เราจำเป็นต้องส่ง ดู? มันเป็นปัญหาเวลาคอมไพล์ - person Jasper Blues; 05.05.2014
comment
@Andrea ต้องบอกว่าไม่มีอะไรทำให้ฉันมีความสุขมากไปกว่าการได้รับโอกาสในการเลี้ยงปืนใหญ่แบบ swizzle :) ลองเข้าไปดูที่ www.typhoonframework.org - เราทำสิ่งแปลกๆ ด้วยรันไทม์ของ ObjC ที่นั่น - person Jasper Blues; 05.05.2014