Swift初心者です。
現在UITableViewControllerを使ってテーブルビューを実装中です(正確には、UITableViewControllerを継承したクラス内にコードを書いています)
UITableViewControllerのソースコードを見ると、UITableViewDataSourceというプロトコルを批准しています。UITableViewDataSourceは、それを批准したクラスに対して
swift
1tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 2tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
という2つのメソッドを実装することを要求していると思うのですが、UITableViewControllerにそれら二つのメソッドが書かれているように見えません。
どういった仕組みでUITableViewControllerとUITableViewDataSourceの関係が成立しているのでしょうか?また、自分の理解しているデリゲートとプロトコルの関係が間違っていればご指摘いただけると幸いです。
###補足情報
Xcode version 8.1
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答3件
0
ベストアンサー
結論から言いますとUITableViewControllerはUITableViewDataSourceに準拠しています。
ポイントは2つあります。
- Xcodeなどで確認しているUIKitの内容はHeaderファイルであること
- UITableViewControllerに関してpublicなものがHeaderに含まれていないこと
UITableViewControllerはUITableViewDataSourceに準拠している
次のようにUITableViewControllerを継承する場合にoverrideしていることからもUITableViewDataSourceのpublic funcが実装されていることが分かります。(他の回答者様も同様の内容について言及されています)
swift
1open class CustomTableViewController: UITableViewController { 2 override public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 3 return 0 4 } 5 6 override public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 7 return UITableViewCell() 8 } 9}
例えば、自らUITableViewDataSourceに準拠させようとすると次のようになります
swift
1open class SampleTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { 2 public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 3 return 0 4 } 5 6 public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 7 return UITableViewCell() 8 } 9}
ヘッダーと実装ファイルの例
public以上のアクセスレベルのものがヘッダーに表示されます。
ここではヘッダーに含まれているものが実装のすべてではないということが分かっていただければ良いです。
実装ファイル
swift
1open class SampleTableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { 2 open var openString: String? 3 public var publicString: String? 4 internal var internalString: String? 5 fileprivate var fileprivateString: String? 6 private var privateString: String? 7 8 override open func viewDidLoad() { 9 super.viewDidLoad() 10 // Do any additional setup after loading the view, typically from a nib. 11 12 } 13 14 override open func didReceiveMemoryWarning() { 15 super.didReceiveMemoryWarning() 16 // Dispose of any resources that can be recreated. 17 } 18 19 public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 20 return 0 21 } 22 23 public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 24 return UITableViewCell() 25 } 26 27 func cannotAccessFromOutsideOfFramework() { 28 } 29}
ヘッダーファイル
swift
1open class SampleTableViewController : UIViewController, UITableViewDelegate, UITableViewDataSource { 2 3 open var openString: String? 4 5 public var publicString: String? 6 7 override open func viewDidLoad() 8 9 override open func didReceiveMemoryWarning() 10 11 public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 12 13 public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 14}
UITableViewControllerヘッダーに含まれるはずのpublic funcは何処へ
上記示したように、UITableViewDataSourceに準拠し以下2つのメソッドを実装していればヘッダーファイルにも表示されるはずです。しかし、UIKitにはそれが含まれていません。
調べて見ましたが、Swiftのみでこの挙動の再現方法は分かりませんでした。
swift
1public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 2public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
生成されるヘッダーファイルの比較
Swiftで実現する方法は分からなかったので、Frameworkとして生成されるヘッダーを比較しました。
以下の3つの場合について示します。尚Sampleプロジェクト新規プロジェクト作成しCocoa Touch Frameworkを追加することでSwift, Objective-Cそれぞれについて作成しました。
- UIKit
- Sampleプロジェクト(Swift)
- Sampleプロジェクト(Objective-C)
Apple公式UIKit
UITableViewController.h
Xcodeに含まれているUITableViewController.hは以下の場所にあります。
bash
1/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UITableViewController.h
中身を見てみると以下の様になっています。
UITableViewController.h
1// 2// UITableViewController.h 3// UIKit 4// 5// Copyright (c) 2008-2016 Apple Inc. All rights reserved. 6// 7#import <Foundation/Foundation.h> 8#import <UIKit/UIViewController.h> 9#import <UIKit/UITableView.h> 10#import <UIKit/UIKitDefines.h> 11 12// Creates a table view with the correct dimensions and autoresizing, setting the datasource and delegate to self. 13// In -viewWillAppear:, it reloads the table's data if it's empty. Otherwise, it deselects all rows (with or without animation) if clearsSelectionOnViewWillAppear is YES. 14// In -viewDidAppear:, it flashes the table's scroll indicators. 15// Implements -setEditing:animated: to toggle the editing state of the table. 16 17NS_ASSUME_NONNULL_BEGIN 18 19NS_CLASS_AVAILABLE_IOS(2_0) @interface UITableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> 20 21- (instancetype)initWithStyle:(UITableViewStyle)style NS_DESIGNATED_INITIALIZER; 22- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil NS_DESIGNATED_INITIALIZER; 23- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; 24 25@property (nonatomic, strong, null_resettable) UITableView *tableView; 26@property (nonatomic) BOOL clearsSelectionOnViewWillAppear NS_AVAILABLE_IOS(3_2); // defaults to YES. If YES, any selection is cleared in viewWillAppear: 27 28@property (nonatomic, strong, nullable) UIRefreshControl *refreshControl NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED; 29 30@end 31 32NS_ASSUME_NONNULL_END
Sampleプロジェクトにて検証した結果
Swiftで書かれたFrameworkではSWIFT_CLASSが使われておりObjective-Cへ変換されています。
この際にSwiftのアクセスレベルに応じてヘッダーが生成されています。
一方でObjective-Cで書かれたFrameworkではヘッダーと実装ファイルが分かれているため、
ヘッダーで定義したものが書き出されています。
上記UITableViewController.hとこれらを鑑みるとUIKitに関しては依然Objective-Cで作成されていると考えられます。
そのため、UITableViewController.hのヘッダーにはたとえpublic funcであってもUITableViewDataSourceに定義されているものは記載されていないと考えられます。
Swift Frameworkで書き出されるヘッダー
Header
1// Generated by Apple Swift version 3.0.1 (swiftlang-800.0.58.6 clang-800.0.42.1) 2#pragma clang diagnostic push 3 4#if defined(__has_include) && __has_include(<swift/objc-prologue.h>) 5# include <swift/objc-prologue.h> 6#endif 7 8--- 9長いので省略 10--- 11 12#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" 13#pragma clang diagnostic ignored "-Wduplicate-method-arg" 14@class UITableView; 15@class UITableViewCell; 16@class NSBundle; 17@class NSCoder; 18 19SWIFT_CLASS("_TtC25FrameworkForTeratail5919325SampleTableViewController") 20@interface SampleTableViewController : UIViewController <UIScrollViewDelegate, UITableViewDataSource, UITableViewDelegate> 21@property (nonatomic, copy) NSString * _Nullable openString; 22@property (nonatomic, copy) NSString * _Nullable publicString; 23- (void)viewDidLoad; 24- (void)didReceiveMemoryWarning; 25- (NSInteger)tableView:(UITableView * _Nonnull)tableView numberOfRowsInSection:(NSInteger)section; 26- (UITableViewCell * _Nonnull)tableView:(UITableView * _Nonnull)tableView cellForRowAtIndexPath:(NSIndexPath * _Nonnull)indexPath; 27- (nonnull instancetype)initWithNibName:(NSString * _Nullable)nibNameOrNil bundle:(NSBundle * _Nullable)nibBundleOrNil OBJC_DESIGNATED_INITIALIZER; 28- (nullable instancetype)initWithCoder:(NSCoder * _Nonnull)aDecoder OBJC_DESIGNATED_INITIALIZER; 29@end 30 31#pragma clang diagnostic pop
Objective-C Frameworkで書き出されるヘッダー
Header
1#import <UIKit/UIKit.h> 2 3@interface OCTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> 4- (id)initWithStyle:(UITableViewStyle)style; 5 6@property (nonatomic, strong) UITableView *tableView; 7@property (nonatomic) BOOL clearsSelectionOnViewWillAppear; 8@end
まとめ
UITableViewControllerのヘッダーにはUITableViewDataSourceのメソッドは含まれていないものの、
UITableViewDataSourceに準拠したメソッドの実装は行われている。
その裏には依然Objective-Cが生きていることを垣間見ることができる。
長文失礼しました、参考になれば幸いです。
投稿2016/12/19 07:05
総合スコア30
0
UITableViewControllerのソースコードを見ると
UIKitのソースコードって公開されてないんじゃ‥?
ちゃんと実装されていると思いますが。(コードはSwift2です)
swift
1let t = UITableViewController() 2print(t.tableView(t.tableView, numberOfRowsInSection: 0)) 3//=> 0
投稿2016/12/19 01:08
総合スコア16731
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
swift
1tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 2tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
上記のメソッドはUITableViewController
クラスのメソッドではありません、UITableViewDataSource
プロトコルのメソッドです。
ですからUITableViewDataSource
に準拠した時には上記の必須のメソッドを実装する必要があります。
※ 以下がUITableViewDataSource
のメソッドでoptional
の付いていない最初の2つが実装必須という事です。
swift
1public protocol UITableViewDataSource : NSObjectProtocol { 2 3 @available(iOS 2.0, *) 4 public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 5 6 @available(iOS 2.0, *) 7 public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 8 9 @available(iOS 2.0, *) 10 optional public func numberOfSections(in tableView: UITableView) -> Int 11 12 @available(iOS 2.0, *) 13 optional public func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 14 15 @available(iOS 2.0, *) 16 optional public func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? 17 18 @available(iOS 2.0, *) 19 optional public func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool 20 21 @available(iOS 2.0, *) 22 optional public func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool 23 24 @available(iOS 2.0, *) 25 optional public func sectionIndexTitles(for tableView: UITableView) -> [String]? 26 27 @available(iOS 2.0, *) 28 optional public func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int 29 30 @available(iOS 2.0, *) 31 optional public func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) 32 33 @available(iOS 2.0, *) 34 optional public func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) 35} 36
自身の定義したクラスがUITableViewController
クラスを継承しているということはUITableViewController
クラスに適用されているUITableViewDataSource
プロトコルのメソッドを実装しなければならないという事です。
swift
1// UITableViewControllerの上にマウスを持って行き⌘ボタンを押したままクリックするとUITableViewControllerの定義に飛べます。 2class TableViewController: UITableViewController { 3 // 自分で定義したクラス 4} 5 6↓ 7 8// 更にUITableViewDataSourceの上にマウスを持って行き⌘ボタンを押したままクリックするとUITableViewDataSourceの定義に飛べます。 9@available(iOS 2.0, *) 10open class UITableViewController : UIViewController, UITableViewDelegate, UITableViewDataSource { 11 12 public init(style: UITableViewStyle) 13 14 public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) 15 public init?(coder aDecoder: NSCoder) 16 17 open var tableView: UITableView! 18 19 @available(iOS 3.2, *) 20 open var clearsSelectionOnViewWillAppear: Bool // defaults to YES. If YES, any selection is cleared in viewWillAppear: 21 22 @available(iOS 6.0, *) 23 open var refreshControl: UIRefreshControl? 24} 25
投稿2016/12/17 16:32
編集2016/12/17 23:07総合スコア8490
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/12/18 05:13
2016/12/18 06:01
2016/12/18 11:17
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/12/19 11:50