कस्टम व्यू कंट्रोलर बनाने के लिए मेरे ऐप में कक्षाओं का पदानुक्रम है।

प्रथम श्रेणी AppViewController है। यह NSViewController का विस्तार करता है और इसमें मेरे सभी व्यू कंट्रोलर के लिए सामान्य तरीके शामिल हैं, जैसे अलर्ट प्रदर्शित करना, डेटाबेस से डेटा पुनर्प्राप्त करना, और बहुत कुछ। यह किसी भी चर को परिभाषित नहीं करता है।

class AppViewController: NSViewController
{
    ...
}

अगली कक्षा ListViewController है और मेरे सभी "सूची" दृश्यों के लिए सामान्य है। ये ऐसे दृश्य हैं जिनमें संबंधित डेटाबेस तालिका के सभी रिकॉर्ड्स की सूची के साथ एक एकल NSTableView होता है। यह AppViewController का विस्तार करता है और सामान्य प्रोटोकॉल के अनुरूप है।

ध्यान दें कि यह वर्ग सामान्य है ताकि यह विभिन्न विचारों और डेटा मॉडल को ठीक से संभाल सके।

class ListViewController<Model: RestModel>: AppViewController,
                                            NSWindowDelegate,
                                            NSTableViewDataSource,
                                            NSTableViewDelegate
{
    ...
}

ListViewController एक NSTableView के लिए IBOutlet सहित कई चरों को परिभाषित करता है। उस आउटलेट को स्टोरीबोर्ड में किसी भी चीज़ से तार-तार नहीं किया गया है। इसे रन-टाइम पर सेट करने की योजना है।

ListViewController विभिन्न कार्यों को भी परिभाषित करता है, जिसमें viewDidLoad (), viewWillAppear (), कई ऐप-विशिष्ट फ़ंक्शन, और इसी तरह शामिल हैं।

अंतिम वर्ग डेटाबेस मॉडल और दृश्य के लिए विशिष्ट है, इस मामले में, ग्राहक देखें। यह ListViewController का विस्तार करता है।

class Clv: ListViewController<CustomerMaster>
{
    ...
}

CustomerMaster एक ठोस वर्ग है जो RestModel प्रोटोकॉल के अनुरूप है।

समस्या:
अजीब बात यह है कि अंतिम वर्ग, सीएलवी, स्टोरीबोर्ड के कस्टम क्लास: क्लास पुल-डाउन मेनू में दिखाई नहीं देता है, जिसका अर्थ है कि मैं इसे अपने विचार के लिए कस्टम क्लास के रूप में निर्दिष्ट नहीं कर सकता।

मैंने इसे बस टाइप करने की कोशिश की, लेकिन इसके परिणामस्वरूप रन-टाइम त्रुटि हुई

इंटरफ़ेस बिल्डर फ़ाइल में अज्ञात वर्ग _TtC9Inventory3Clv ...

यदि मैं ListViewController वर्ग परिभाषा से <मॉडल: RestModel> को हटाता हूं और Clv वर्ग परिभाषा से को भी हटाता हूं, तो Clv वर्ग तब कक्षा मेनू में दिखाई देता है (बेशक यह वास्तव में मदद नहीं करता है, केवल एक अवलोकन) .

AppViewController और ListViewController दोनों उस मेनू में करते हैं दिखाई देते हैं।

मैं नुकसान में हूँ।

2
David Patterson 29 अगस्त 2017, 23:38

1 उत्तर

सबसे बढ़िया उत्तर

@vikingosegundo द्वारा उत्तर, Xcode की शिकायत की व्याख्या करते हुए और आम तौर पर बहुत जानकारीपूर्ण होने के कारण, मेरी विशेष समस्या को हल करने में मेरी मदद नहीं की। मेरा प्रोजेक्ट Xcode 8.3.3 में शुरू किया गया था और मेरे पास स्टोरीबोर्ड में पहले से ही बहुत सारी विंडो और दृश्य हैं, इसलिए मैं वास्तव में स्टोरीबोर्ड/जेनेरिक मुद्दे को छोड़ना या काम करना नहीं चाहता।

ऐसा कहा जा रहा है कि, मैंने कुछ और शोध किया और यह महसूस किया कि बहुत से लोग वर्ग विरासत के लिए प्रतिनिधिमंडल पसंद करते हैं इसलिए मैंने उस दृष्टिकोण का पता लगाने का फैसला किया। मैं कुछ ऐसा काम करने में सक्षम था जो मेरी जरूरतों को पूरा करता हो।

मैं यहां एक सरल, लेकिन कार्यात्मक दृष्टिकोण प्रस्तुत करता हूं।

पहला, एक प्रोटोकॉल जिसका हमारे डेटा मॉडल को पालन करना चाहिए:

protocol RestModel
{
  static var entityName: String { get }
  var id: Int { get }
}

अगला, एक डेटा मॉडल:

///
/// A dummy model for testing. It has two properties: an ID and a  name.
///
class ModelOne: RestModel
{
  static var entityName: String = "ModelOne"
  var id: Int
  var name: String

  init(_ id: Int, _ name: String)
  {
    self.id = id
    self.name = name
  }
}

फिर, एक प्रोटोकॉल जिसके लिए हमारे आधार वर्ग का विस्तार करने वाले सभी वर्गों के अनुरूप होना चाहिए:

///
/// Protocol: ListViewControllerDelegate
///
/// All classes that extend BaseListViewController must conform to this
/// protocol. This allows us to separate all knowledge of the actual data
/// source, record formats, etc. into a view-specific controller.
///
protocol ListViewControllerDelegate: class
{
  ///
  /// The actual table view object. This must be defined in the extending class
  /// as @IBOutlet weak var tableView: NSTableView!. The base class saves a weak
  /// reference to this variable in one of its local variables and uses that
  /// variable to access the actual table view object.
  ///
  weak var tableView: NSTableView! { get }

  ///
  /// This method must perform whatever I/O is required to load the data for the
  /// table view. Loading the data is assumed to be asyncronous so the method
  /// must accept a closure which must be called after the data has been loaded.
  ///
  func loadRecords()

  ///
  /// This method must simply return the number of rows in the data set.
  ///
  func numberOfRows() -> Int

  ///
  /// This method must return the text that is to be displayed in the specified
  /// cell. 
  /// - parameters:
  ///   - row:    The row number (as supplied in the call to tableView(tableView:viewFor:row:).
  ///   - col:    The column identifier (from tableColumn.identifier).
  /// - returns:  String
  ///
  func textForCell(row: Int, col: String) -> String

} // ListViewControllerDelegate protocol

अब वास्तविक आधार वर्ग:

class BaseListViewController: NSViewController,  
                              NSTableViewDataSource,  
                              NSTableViewDelegate
{
  //
  // The instance of the extending class. Like most delegate variables in Cocoa
  // applications, this variable must be set by the delegate (the extending
  // class, in this case).
  //
  weak var delegate: ListViewControllerDelegate?

  //
  // The extending class' actual table view object.
  //
  weak var delegateTableView: NSTableView!

  //
  // Calls super.viewDidLoad()
  // Gets a reference to the extending class' table view object.
  // Sets the data source and delegate for the table view.
  // Calls the delegate's loadRecords() method.
  //
  override func viewDidLoad()
  {
    super.viewDidLoad()
    delegateTableView = delegate?.tableView
    delegateTableView.dataSource = self
    delegateTableView.delegate = self
    delegate?.loadRecords()
    delegateTableView.reloadData()
  }


  //
  // This is called by the extending class' table view object to retreive the
  // number of rows in the data set.
  //
  func numberOfRows(in tableView: NSTableView) -> Int
  {
    return (delegate?.numberOfRows())!
  }


  //
  // This is called by the extending class' table view to retrieve a view cell
  // for each column/row in the table. We call the delegate's textForCell(row:col:)
  // method to retrieve the text and then create a view cell with that as its
  // contents.
  //
  func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
  {
    if let col = tableColumn?.identifier, let text = delegate?.textForCell(row: row, col: col)
    {
      if let cell = delegate?.tableView.make(withIdentifier: (tableColumn?.identifier)!, owner: nil) as? NSTableCellView
      {
        cell.textField?.stringValue = text
        return cell
      }
    }
    return nil
  }
} // BaseListViewController{}

और, अंत में, एक विस्तृत वर्ग:

///
/// A concrete example class that extends BaseListViewController{}.
/// It loadRecords() method simply uses a hard-coded list.
/// This is the class that is specified in the IB.
///
class ViewOne: BaseListViewController, ListViewControllerDelegate
{
  var records: [ModelOne] = []

  //
  // The actual table view in our view.
  //
  @IBOutlet weak var tableView: NSTableView!

  override func viewDidLoad()
  {
    super.delegate = self
    super.viewDidLoad()
  }

  func loadRecords()
  {
    records =
    [
      ModelOne(1, "AAA"),
      ModelOne(2, "BBB"),
      ModelOne(3, "CCC"),
      ModelOne(4, "DDD"),
    ]
  }

  func numberOfRows() -> Int
  {
    return records.count
  }

  func textForCell(row: Int, col: String) -> String
  {
    switch col
    {
    case "id":
      return "\(records[row].id)"

    case "name":
      return records[row].name

    default:
      return ""
    }
  }
} // ViewOne{}

बेशक, यह एक सरलीकृत प्रोटोटाइप है। वास्तविक दुनिया के कार्यान्वयन में, डेटाबेस, वेब सेवा, या कुछ इस तरह से डेटा को एसिंक्रोनस रूप से लोड करने के बाद रिकॉर्ड लोड करना और तालिका को अपडेट करना बंद हो जाएगा।

मेरा पूरा प्रोटोटाइप दो मॉडल और दो व्यू कंट्रोलर को परिभाषित करता है जो बेसलिस्ट व्यू क्लास का विस्तार करते हैं। यह वांछित के रूप में काम करता है। बेस क्लास के उत्पादन संस्करण में कई अन्य विधियां शामिल होंगी (यही कारण है कि यह पहली जगह बेस क्लास बनना चाहता था :-)

0
David Patterson 1 सितंबर 2017, 00:40