回答:
UITableViewのbackgroundViewプロパティはあなたの友達です。
その中viewDidLoad
またはどこでもreloadData
、テーブルが空かどうかを判断し、UILabelを含むUIViewでUITableViewのbackgroundViewプロパティを更新するか、単にnilに設定する必要があります。それでおしまい。
もちろん、UITableViewのデータソースに二重の役割を持たせ、特別な「リストが空」のセルを返すようにすることもできます。突然numberOfRowsInSection:(NSInteger)section
、他のセクションの行数も計算する必要があり、行が空であることを確認することも求められませんでした。また、メッセージが空の特別なセルを作成する必要があります。また、おそらく空のメッセージに対応するためにセルの高さを変更する必要があることを忘れないでください。これはすべて実行可能ですが、バンドエイドの上にバンドエイドがあるようです。
backgroundView
隠しプロパティの切り替えは、進むべき道です。ではDZNEmptyDataSet
、私たちは、その使用する必要がありますemptyDataSetSource
tableView.backgroundView!.userInteraction = true
、行の後にを設定するtableView.backgroundView = constructMyViewWithButtons()
か、設定する必要があります。
ジョンストンの答えと同じですが、私はそれを拡張として選択しました:
import UIKit
extension UITableView {
func setEmptyMessage(_ message: String) {
let messageLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = .black
messageLabel.numberOfLines = 0
messageLabel.textAlignment = .center
messageLabel.font = UIFont(name: "TrebuchetMS", size: 15)
messageLabel.sizeToFit()
self.backgroundView = messageLabel
self.separatorStyle = .none
}
func restore() {
self.backgroundView = nil
self.separatorStyle = .singleLine
}
}
使用法:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if things.count == 0 {
self.tableView.setEmptyMessage("My Message")
} else {
self.tableView.restore()
}
return things.count
}
func numberOfSections(in tableView: UITableView) -> Int
方法
ここでの回答に基づいて、ここで私が作成したクイッククラスを使用して、で使用できますUITableViewController
。
import Foundation
import UIKit
class TableViewHelper {
class func EmptyMessage(message:String, viewController:UITableViewController) {
let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: self.view.bounds.size.width, height: self.view.bounds.size.height))
let messageLabel = UILabel(frame: rect)
messageLabel.text = message
messageLabel.textColor = UIColor.blackColor()
messageLabel.numberOfLines = 0;
messageLabel.textAlignment = .Center;
messageLabel.font = UIFont(name: "TrebuchetMS", size: 15)
messageLabel.sizeToFit()
viewController.tableView.backgroundView = messageLabel;
viewController.tableView.separatorStyle = .None;
}
}
あなたの中でUITableViewController
これを呼び出すことができますnumberOfSectionsInTableView(tableView: UITableView) -> Int
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if projects.count > 0 {
return 1
} else {
TableViewHelper.EmptyMessage("You don't have any projects yet.\nYou can create up to 10.", viewController: self)
return 0
}
}
http://www.appcoda.com/pull-to-refresh-uitableview-empty/の少しの助けを借りて
viewController.tableView.separatorStyle = .none
必須ではありません。
次のライブラリをお勧めします:DZNEmptyDataSet
プロジェクトに追加する最も簡単な方法は、次のようにCocaopodsで使用することです。 pod 'DZNEmptyDataSet'
TableViewControllerに次のインポートステートメント(Swift)を追加します。
import DZNEmptyDataSet
その後に必ずあなたのクラスの適合を作るDNZEmptyDataSetSource
と、DZNEmptyDataSetDelegate
そのように:
class MyTableViewController: UITableViewController, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate
あなたにはviewDidLoad
次のコード行を追加します。
tableView.emptyDataSetSource = self
tableView.emptyDataSetDelegate = self
tableView.tableFooterView = UIView()
emptystateを表示するために必要なのは次のとおりです。
//Add title for empty dataset
func titleForEmptyDataSet(scrollView: UIScrollView!) -> NSAttributedString! {
let str = "Welcome"
let attrs = [NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)]
return NSAttributedString(string: str, attributes: attrs)
}
//Add description/subtitle on empty dataset
func descriptionForEmptyDataSet(scrollView: UIScrollView!) -> NSAttributedString! {
let str = "Tap the button below to add your first grokkleglob."
let attrs = [NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleBody)]
return NSAttributedString(string: str, attributes: attrs)
}
//Add your image
func imageForEmptyDataSet(scrollView: UIScrollView!) -> UIImage! {
return UIImage(named: "MYIMAGE")
}
//Add your button
func buttonTitleForEmptyDataSet(scrollView: UIScrollView!, forState state: UIControlState) -> NSAttributedString! {
let str = "Add Grokkleglob"
let attrs = [NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleCallout)]
return NSAttributedString(string: str, attributes: attrs)
}
//Add action for button
func emptyDataSetDidTapButton(scrollView: UIScrollView!) {
let ac = UIAlertController(title: "Button tapped!", message: nil, preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "Hurray", style: .Default, handler: nil))
presentViewController(ac, animated: true, completion: nil)
}
これらのメソッドは必須ではなく、ボタンなしで空の状態を表示することも可能です。
Swift 4の場合
// MARK: - Deal with the empty data set
// Add title for empty dataset
func title(forEmptyDataSet _: UIScrollView!) -> NSAttributedString! {
let str = "Welcome"
let attrs = [NSAttributedStringKey.font: UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline)]
return NSAttributedString(string: str, attributes: attrs)
}
// Add description/subtitle on empty dataset
func description(forEmptyDataSet _: UIScrollView!) -> NSAttributedString! {
let str = "Tap the button below to add your first grokkleglob."
let attrs = [NSAttributedStringKey.font: UIFont.preferredFont(forTextStyle: UIFontTextStyle.body)]
return NSAttributedString(string: str, attributes: attrs)
}
// Add your image
func image(forEmptyDataSet _: UIScrollView!) -> UIImage! {
return UIImage(named: "MYIMAGE")
}
// Add your button
func buttonTitle(forEmptyDataSet _: UIScrollView!, for _: UIControlState) -> NSAttributedString! {
let str = "Add Grokkleglob"
let attrs = [NSAttributedStringKey.font: UIFont.preferredFont(forTextStyle: UIFontTextStyle.callout), NSAttributedStringKey.foregroundColor: UIColor.white]
return NSAttributedString(string: str, attributes: attrs)
}
// Add action for button
func emptyDataSetDidTapButton(_: UIScrollView!) {
let ac = UIAlertController(title: "Button tapped!", message: nil, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "Hurray", style: .default, handler: nil))
present(ac, animated: true, completion: nil)
}
これを行う1つの方法は1
、行の数がゼロのときに返されるようにデータソースを変更し、tableView:cellForRowAtIndexPath:
メソッドで特殊な目的のセル(おそらく異なるセル識別子を持つ)を生成することです。
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger actualNumberOfRows = <calculate the actual number of rows>;
return (actualNumberOfRows == 0) ? 1 : actualNumberOfRows;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger actualNumberOfRows = <calculate the actual number of rows>;
if (actualNumberOfRows == 0) {
// Produce a special cell with the "list is now empty" message
}
// Produce the correct cell the usual way
...
}
誰かが最終的にゼロチェックの挿入を忘れるので、維持する必要のある複数のテーブルビューコントローラーがある場合、これは多少複雑になる可能性があります。より良いアプローチはUITableViewDataSource
、常に構成可能なメッセージを含む単一の行を返す実装の個別の実装を作成することです(これをと呼びましょうEmptyTableViewDataSource
)。テーブルビューコントローラーによって管理されているデータが変更されると、変更を管理するコードがデータが空かどうかをチェックします。空でない場合は、テーブルビューコントローラーに通常のデータソースを設定します。それ以外の場合はEmptyTableViewDataSource
、適切なメッセージで構成されているのインスタンスで設定します。
deleteRowsAtIndexPaths:withRowAnimation:
、行が削除されたテーブルでこれを行うと問題が発生しましたnumberOfRowsInSection
。
これにはtitleForFooterInSectionメッセージを使用しています。これが最適ではないかどうかはわかりませんが、動作します。
-(NSString*)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
NSString *message = @"";
NSInteger numberOfRowsInSection = [self tableView:self.tableView numberOfRowsInSection:section ];
if (numberOfRowsInSection == 0) {
message = @"This list is now empty";
}
return message;
}
return tableView.numberOfRowsInSection(section) == 0 ? "This list is now empty" : nil
より安全なソリューションのために:
extension UITableView {
func setEmptyMessage(_ message: String) {
guard self.numberOfRows() == 0 else {
return
}
let messageLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = .black
messageLabel.numberOfLines = 0;
messageLabel.textAlignment = .center;
messageLabel.font = UIFont.systemFont(ofSize: 14.0, weight: UIFontWeightMedium)
messageLabel.sizeToFit()
self.backgroundView = messageLabel;
self.separatorStyle = .none;
}
func restore() {
self.backgroundView = nil
self.separatorStyle = .singleLine
}
public func numberOfRows() -> Int {
var section = 0
var rowCount = 0
while section < numberOfSections {
rowCount += numberOfRows(inSection: section)
section += 1
}
return rowCount
}
}
そしてUICollectionView
同様に:
extension UICollectionView {
func setEmptyMessage(_ message: String) {
guard self.numberOfItems() == 0 else {
return
}
let messageLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = .black
messageLabel.numberOfLines = 0;
messageLabel.textAlignment = .center;
messageLabel.font = UIFont.systemFont(ofSize: 18.0, weight: UIFontWeightSemibold)
messageLabel.sizeToFit()
self.backgroundView = messageLabel;
}
func restore() {
self.backgroundView = nil
}
public func numberOfItems() -> Int {
var section = 0
var itemsCount = 0
while section < self.numberOfSections {
itemsCount += numberOfItems(inSection: section)
section += 1
}
return itemsCount
}
}
より一般的なソリューション:
protocol EmptyMessageViewType {
mutating func setEmptyMessage(_ message: String)
mutating func restore()
}
protocol ListViewType: EmptyMessageViewType where Self: UIView {
var backgroundView: UIView? { get set }
}
extension UITableView: ListViewType {}
extension UICollectionView: ListViewType {}
extension ListViewType {
mutating func setEmptyMessage(_ message: String) {
let messageLabel = UILabel(frame: CGRect(x: 0,
y: 0,
width: self.bounds.size.width,
height: self.bounds.size.height))
messageLabel.text = message
messageLabel.textColor = .black
messageLabel.numberOfLines = 0
messageLabel.textAlignment = .center
messageLabel.font = UIFont(name: "TrebuchetMS", size: 16)
messageLabel.sizeToFit()
backgroundView = messageLabel
}
mutating func restore() {
backgroundView = nil
}
}
セルの後のTableView内にUITextViewをドラッグアンドドロップすることのみをお勧めします。ViewControllerへの接続を作成し、必要に応じて(たとえば、テーブルがリロードされるたびに)非表示/表示します。
backgroundViewの使用は問題ありませんが、Mail.appのようにうまくスクロールできません。
のビュー階層の外側にビューを追加しましたtableViewController
。
次に、次のコードを使用しましたtableView:numberOfRowsInSection:
:
if someArray.count == 0 {
// Show Empty State View
self.tableView.addSubview(self.emptyStateView)
self.emptyStateView.center = self.view.center
self.emptyStateView.center.y -= 60 // rough calculation here
self.tableView.separatorColor = UIColor.clear
} else if self.emptyStateView.superview != nil {
// Empty State View is currently visible, but shouldn't
self.emptyStateView.removeFromSuperview()
self.tableView.separatorColor = nil
}
return someArray.count
基本的にemptyStateView
は、tableView
オブジェクトのサブビューとしてを追加しました。セパレータはビューと重なるので、色をに設定しましたclearColor
。デフォルトのセパレータの色に戻すには、単にに設定しnil
ます。
tableHeaderView
、それに合わせてサイズ変更することを確認します。
Appleによると、Container View Controllerを使用するのが正しい方法です。
空の状態ビューをすべて別のストーリーボードに配置しました。その下には、それぞれ独自のUIViewControllerサブクラスがあります。ルートビューの直下にコンテンツを追加します。アクション/ボタンが必要な場合は、すでにそれを処理するコントローラーがあります。
次に、そのStoryboardから目的のビューコントローラーをインスタンス化し、それを子ビューコントローラーとして追加し、コンテナービューをtableViewの階層(サブビュー)に追加します。空の状態ビューもスクロール可能になります。これは快適で、プルトゥリフレッシュを実装できます。
実装方法については、「コンテンツへの子ビューコントローラの追加」の章をお読みください。
ちょうど子ビューフレームを設定する
(0, 0, tableView.frame.width, tableView.frame.height)
と、物事は中央に配置され、適切に配置されます。
まず、他の一般的なアプローチの問題。
BackgroundView
UILabelに設定するという単純なケースを使用すると、背景ビューは適切に中央に配置されません。
メッセージを表示するためのセル、ヘッダー、またはフッター
これは機能コードに干渉し、奇妙なエッジケースを引き起こします。メッセージを完全に中央に配置したい場合は、さらに複雑になります。
独自のテーブルビューコントローラーをロールする
refreshControlなどの組み込み機能を失い、ホイールを再発明します。維持可能な最良の結果を得るには、UITableViewControllerを使用してください。
子ビューコントローラーとしてUITableViewControllerを追加する
iOS 7以降でcontentInsetの問題が発生するような気がします。なぜ複雑なのでしょうか。
私の解決策
私が思いついた最善の解決策(そして当然のことながら、これは理想的ではありません)は、スクロールビューの上に座ってそれに応じて動作できる特別なビューを作成することです。これは明らかにiOS7ではcontentInsetの狂気で複雑になりますが、それは可能です。
注意しなければならないこと:
これを1つのUIViewサブクラスで一度把握したら、スピナーのロード、ビューの無効化、エラーメッセージの表示など、すべてに使用できます。
addSubView
と、自動レイアウトで常に問題となるテーブルビューにアタッチされます。
これが最良のシンプルなソリューションです。
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 60)];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 60)];
label.text = @"This list is empty";
label.center = self.view.center;
label.textAlignment = NSTextAlignmentCenter;
[view addSubview:label];
self.tableView.backgroundView = view;
空のリストのメッセージ、そのUITableViewまたはUICollectionViewを表示します。
extension UIScrollView {
func showEmptyListMessage(_ message:String) {
let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: self.bounds.size.width, height: self.bounds.size.height))
let messageLabel = UILabel(frame: rect)
messageLabel.text = message
messageLabel.textColor = .black
messageLabel.numberOfLines = 0
messageLabel.textAlignment = .center
messageLabel.font = UIFont.systemFont(ofSize: 15)
messageLabel.sizeToFit()
if let `self` = self as? UITableView {
self.backgroundView = messageLabel
self.separatorStyle = .none
} else if let `self` = self as? UICollectionView {
self.backgroundView = messageLabel
}
}
}
使用法:
if cellsViewModels.count == 0 {
self.tableView.showEmptyListMessage("No Product In List!")
}
または:
if cellsViewModels.count == 0 {
self.collectionView?.showEmptyListMessage("No Product In List!")
}
忘れないでください: 更新後にデータが来る場合に備えて、メッセージラベルを忘れずに削除してください。
ストーリーボードでtableviewControllerシーンを選択します
UIView Add label with your message(eg:No Data)をドラッグアンドドロップ
TableViewControllerにUIViewのアウトレット(たとえばyournoDataViewなど)を作成します。
そしてviewDidLoadで
self.tableView.backgroundView = yourNoDataView
Swift 4.2の使用
func numberOfSections(in tableView: UITableView) -> Int
{
var numOfSections: Int = 0
if self.medArray.count > 0
{
tableView.separatorStyle = .singleLine
numOfSections = 1
tableView.backgroundView = nil
}
else
{
let noDataLabel: UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: tableView.bounds.size.height))
noDataLabel.text = "No Medicine available.Press + to add New Pills "
noDataLabel.textColor = UIColor.black
noDataLabel.textAlignment = .center
tableView.backgroundView = noDataLabel
tableView.separatorStyle = .none
}
return numOfSections
}
これを基本クラスに追加できます。
var messageLabel = UILabel()
func showNoDataMessage(msg: String) {
let rect = CGRect(origin: CGPoint(x: 0, y :self.view.center.y), size: CGSize(width: self.view.bounds.width - 16, height: 50.0))
messageLabel = UILabel(frame: rect)
messageLabel.center = self.view.center
messageLabel.text = msg
messageLabel.numberOfLines = 0
messageLabel.textColor = Colors.grayText
messageLabel.textAlignment = .center;
messageLabel.font = UIFont(name: "Lato-Regular", size: 17)
self.view.addSubview(messageLabel)
self.view.bringSubviewToFront(messageLabel)
}
APIからデータを取得するときに、クラスでこのように表示します。
func populateData(dataSource : [PRNJobDataSource]){
self.dataSource = dataSource
self.tblView.reloadData()
if self.dataSource.count == 0 {self.showNoDataMessage(msg: "No data found.")}
}
このように非表示にします。
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.dataSource.count > 0 {self.hideNoDataMessage()}
return dataSource.count
}
func hideNoDataMessage(){
messageLabel.removeFromSuperview()
}
Swiftバージョンですが、より良くシンプルな形式です。** 3.0
私はそれがあなたの目的に役立つことを願っています......
UITableViewController内。
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.isActive && searchController.searchBar.text != "" {
if filteredContacts.count > 0 {
self.tableView.backgroundView = .none;
return filteredContacts.count
} else {
Helper.EmptyMessage(message: ConstantMap.NO_CONTACT_FOUND, viewController: self)
return 0
}
} else {
if contacts.count > 0 {
self.tableView.backgroundView = .none;
return contacts.count
} else {
Helper.EmptyMessage(message: ConstantMap.NO_CONTACT_FOUND, viewController: self)
return 0
}
}
}
関数を持つヘルパークラス:
/* Description: This function generate alert dialog for empty message by passing message and
associated viewcontroller for that function
- Parameters:
- message: message that require for empty alert message
- viewController: selected viewcontroller at that time
*/
static func EmptyMessage(message:String, viewController:UITableViewController) {
let messageLabel = UILabel(frame: CGRect(x: 0, y: 0, width: viewController.view.bounds.size.width, height: viewController.view.bounds.size.height))
messageLabel.text = message
let bubbleColor = UIColor(red: CGFloat(57)/255, green: CGFloat(81)/255, blue: CGFloat(104)/255, alpha :1)
messageLabel.textColor = bubbleColor
messageLabel.numberOfLines = 0;
messageLabel.textAlignment = .center;
messageLabel.font = UIFont(name: "TrebuchetMS", size: 18)
messageLabel.sizeToFit()
viewController.tableView.backgroundView = messageLabel;
viewController.tableView.separatorStyle = .none;
}
おそらく最高の解決策ではありませんが、テーブルの下部にラベルを付けるだけでこれを行いました。行が0の場合は、テキストを割り当てます。かなり簡単で、数行のコードで実行しようとしていることを実現できます。
テーブルに2つのセクションがあります(仕事と学校)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (jobs.count == 0 && schools.count == 0) {
emptyLbl.text = "No jobs or schools"
} else {
emptyLbl.text = ""
}
これを行う最も簡単で迅速な方法は、tableViewの下のサイドパネルにラベルをドラッグすることです。ラベルとtableViewのアウトレットを作成し、ifステートメントを追加して、必要に応じてラベルとテーブルを非表示および表示します。または、tableView.tableFooterView = UIView(frame:CGRect.zero)thisをviewDidLoad()に追加して、空のテーブルに、テーブルと背景のビューが同じ色の場合は非表示であるという認識を与えることができます。
カウントを手動で確認する必要がないようにいくつかの変更を加えました。また、次のようにメッセージのサイズが大きくても問題が起こらないように、ラベルに制約を追加しました。
extension UITableView {
fileprivate func configureLabelLayout(_ messageLabel: UILabel) {
messageLabel.translatesAutoresizingMaskIntoConstraints = false
let labelTop: CGFloat = CGFloat(UIDevice.current.userInterfaceIdiom == .pad ? 25:15)
messageLabel.topAnchor.constraint(equalTo: backgroundView?.topAnchor ?? NSLayoutAnchor(), constant: labelTop).isActive = true
messageLabel.widthAnchor.constraint(equalTo: backgroundView?.widthAnchor ?? NSLayoutAnchor(), constant: -20).isActive = true
messageLabel.centerXAnchor.constraint(equalTo: backgroundView?.centerXAnchor ?? NSLayoutAnchor(), constant: 0).isActive = true
}
fileprivate func configureLabel(_ message: String) {
let messageLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.size.width, height: self.bounds.size.height))
messageLabel.textColor = .black
messageLabel.numberOfLines = 0
messageLabel.textAlignment = .center
let fontSize = CGFloat(UIDevice.current.userInterfaceIdiom == .pad ? 25:15)
let font: UIFont = UIFont(name: "MyriadPro-Regular", size: fontSize) ?? UIFont()
messageLabel.font = font
messageLabel.text = message
self.backgroundView = UIView()
self.backgroundView?.addSubview(messageLabel)
configureLabelLayout(messageLabel)
self.separatorStyle = .none
}
func setEmptyMessage(_ message: String, _ isEmpty: Bool) {
if isEmpty { // instead of making the check in every TableView DataSource in the project
configureLabel(message)
}
else {
restore()
}
}
func restore() {
self.backgroundView = nil
self.separatorStyle = .singleLine
}
}
使用法
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let message: String = "The list is empty."
ticketsTableView.setEmptyMessage(message, tickets.isEmpty)
return self.tickets.count
}
または、少しカスタマイズ可能な軽量ライブラリを使用することもできます