web-dev-qa-db-ja.com

UITableViewStyle PlainでUITableViewのフローティングヘッダーを無効にすることは可能ですか?

UITableViewを使用してコンテンツの「ページ」をレイアウトしています。テーブルビューのヘッダーを使用して特定の画像などをレイアウトします。スタイルがUITableViewStyleGroupedに設定されている場合、フロートではなく静的なままである場合は、それを選択します。

他にUITableViewStyleGroupedを使用して、これを行う方法はありますか?すべてのセルにマージンを追加し、各セルの背景ビューを無効にする必要があるため、グループ化の使用を避けたいと思います。レイアウトを完全に制御したい。理想的には「UITableViewStyleBareBones」ですが、ドキュメントにはそのオプションが表示されませんでした...

どうもありがとう、

155
Tricky

カスタムセルを使用してヘッダー行を作成することで、これを偽造できるはずです。これらは、テーブルビューの他のセルと同様にスクロールします。

cellForRowAtIndexPathにいくつかのロジックを追加するだけで、ヘッダー行の場合に正しいセルタイプを返すことができます。

ただし、おそらく自分でセクションを管理する必要があります。つまり、すべてを1つのセクションに入れて、ヘッダーを偽造する必要があります。 (ヘッダービューの非表示ビューを返すこともできますが、それが機能するかどうかはわかりません)

26
frankodwyer

これを達成するためのおそらく簡単な方法:

Objective-C:

CGFloat dummyViewHeight = 40;
UIView *dummyView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, dummyViewHeight)];
self.tableView.tableHeaderView = dummyView;
self.tableView.contentInset = UIEdgeInsetsMake(-dummyViewHeight, 0, 0, 0);

迅速:

let dummyViewHeight = CGFloat(40)
self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: dummyViewHeight))
self.tableView.contentInset = UIEdgeInsetsMake(-dummyViewHeight, 0, 0, 0)

セクションヘッダーは、通常のセルと同じようにスクロールします。

312
samvermette

(間違ったテーブルスタイルのためにここに来た人のために)属性スタイルインスペクターまたはコードを介して、プレーンスタイルをグループ化に変更します。

let tableView = UITableView(frame: .zero, style: .grouped)
210
david72

WARNING:このソリューションは、予約済みのAPIメソッドを実装します。これにより、アプリがAppleによってAppStoreでの配布が承認されない場合があります。

my blog に浮かぶセクションヘッダーを有効にするプライベートメソッドについて説明しました。

基本的に、2つのメソッドでUITableViewをサブクラス化し、NOを返すだけです。

- (BOOL)allowsHeaderViewsToFloat;
- (BOOL)allowsFooterViewsToFloat;
72
XAK

わかりました、遅いのはわかっていますが、やらなければなりませんでした。実用的なソリューションを探すまでに10時間を費やしましたが、完全な答えは見つかりませんでした。いくつかのヒントが見つかりましたが、初心者が理解するのは困難です。だから私は2セントを入れて答えを記入しなければなりませんでした。

いくつかの答えで提案されているように、私が実装できた唯一の実用的な解決策は、テーブルビューに通常のセルを挿入してセクションヘッダーとして処理することですが、それを達成するより良い方法はこれらのセルをすべてのセクションの行0。このようにして、これらのカスタムの非フローティングヘッダーを非常に簡単に処理できます。

したがって、手順は次のとおりです。

  1. UITableViewStylePlainスタイルでUITableViewを実装します。

    -(void) loadView
    {
        [super loadView];
    
        UITableView *tblView =[[UITableView alloc] initWithFrame:CGRectMake(0, frame.Origin.y, frame.size.width, frame.size.height-44-61-frame.Origin.y) style:UITableViewStylePlain];
        tblView.delegate=self;
        tblView.dataSource=self;
        tblView.tag=2;
        tblView.backgroundColor=[UIColor clearColor];
        tblView.separatorStyle = UITableViewCellSeparatorStyleNone;
    }
    
  2. TitleForHeaderInSectionを通常どおり実装します(独自のロジックを使用してこの値を取得できますが、標準のデリゲートを使用することを好みます)。

    - (NSString *)tableView: (UITableView *)tableView titleForHeaderInSection:(NSInteger)section
    {
        NSString *headerTitle = [sectionArray objectAtIndex:section];
        return headerTitle;
    }
    
  3. 通常どおりnumberOfSectionsInTableViewを実装します

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
    {
        int sectionCount = [sectionArray count];
        return sectionCount;
    }
    
  4. 通常どおりnumberOfRowsInSectionを実装します。

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
    {
        int rowCount = [[cellArray objectAtIndex:section] count];
        return rowCount +1; //+1 for the extra row which we will fake for the Section Header
    }
    
  5. HeightForHeaderInSectionに0.0fを返します。

    - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
    {
        return 0.0f;
    }
    
  6. ViewForHeaderInSectionを実装しないでください。 nilを返す代わりに、メソッドを完全に削除します。

  7. HeightForRowAtIndexPath内。 if(indexpath.row == 0)を確認し、セクションヘッダーの目的のセルの高さを返します。それ以外の場合は、セルの高さを返します。

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if(indexPath.row == 0)
        {
            return 80; //Height for the section header
        }
        else
        {
            return 70; //Height for the normal cell
        }
    }
    
  8. 次に、cellForRowAtIndexPathで、if(indexpath.row == 0)を確認し、セクションヘッダーを希望どおりにセルを実装し、選択スタイルをnoneに設定します。 ELSEは、通常のセルにしたいようにセルを実装します。

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row == 0)
        {
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SectionCell"];
            if (cell == nil)
            {
                cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"SectionCell"] autorelease];
                cell.selectionStyle = UITableViewCellSelectionStyleNone; //So that the section header does not appear selected
    
                cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SectionHeaderBackground"]];
            }
    
            cell.textLabel.text = [tableView.dataSource tableView:tableView titleForHeaderInSection:indexPath.section];
    
            return cell;
        }
        else
        {
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    
            if (cell == nil) 
            {
                cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"] autorelease];
                cell.selectionStyle = UITableViewCellSelectionStyleGray; //So that the normal cell looks selected
    
                cell.backgroundView =[[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"CellBackground"]]autorelease];
                cell.selectedBackgroundView=[[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SelectedCellBackground"]] autorelease];
            }
    
            cell.textLabel.text = [[cellArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row -1]; //row -1 to compensate for the extra header row
    
            return cell;
        }
    }
    
  9. ここでwillSelectRowAtIndexPathを実装し、indexpath.row == 0の場合はnilを返します。これにより、didSelectRowAtIndexPathがセクションヘッダー行に対して発生しないようになります。

    - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row == 0)
        {
            return nil;
        }
    
        return indexPath;
    }
    
  10. 最後にdidSelectRowAtIndexPathで、if(indexpath.row!= 0)を確認して続行します。

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row != 0)
        {
            int row = indexPath.row -1; //Now use 'row' in place of indexPath.row
    
            //Do what ever you want the selection to perform
        }
    }
    

これで完了です。これで、完全にスクロールする非フローティングセクションヘッダーができました。

28
Sumit Anantwar

Interface Builderで、問題のあるTable Viewをクリックします

problem table view

次に、Attributes Inspectorに移動し、Style PlainをGroupedに変更します;)Easy

easy

25

UITableViewStyleGroupedの興味深い点は、tableViewがスタイルをcellsに追加し、TableViewに追加しないことです。

スタイルは、セクション内のセルの位置に応じて異なる背景の描画を処理するUIGroupTableViewCellBackgroundというクラスとして、セルにbackgroundViewとして追加されます。

したがって、非常に簡単な解決策は、UITableViewStyleGroupedを使用し、テーブルのbackgroundColorをclearColorに設定し、cellForRowのセルのbackgroundViewを単純に置き換えることです。

cell.backgroundView = [[[UIView alloc] initWithFrame:cell.bounds] autorelease];
17
adamsiton

TableViewスタイルを変更します。

self.tableview = [[UITableView alloc] initwithFrame:frame style:UITableViewStyleGrouped];

UITableViewのAppleドキュメントに従って:

UITableViewStylePlain-プレーンテーブルビュー。セクションのヘッダーまたはフッターはインラインセパレーターとして表示され、テーブルビューがスクロールされるとフロートします。

UITableViewStyleGrouped-セクションが行の個別のグループを表示するテーブルビュー。セクションのヘッダーとフッターはフロートしません。

12
Aks

別のトリッキーな方法があります。主なアイデアは、セクション番号を2倍にすることです。最初のヘッダーはheaderViewのみを表示し、2番目のヘッダーは実際のセルを表示します。

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return sectionCount * 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (section%2 == 0) {
        return 0;
    }
    return _rowCount;
}

次に必要なのは、headerInSectionデリゲートを実装することです。

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    if (section%2 == 0) {
        //return headerview;
    }
    return nil;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    if (section%2 == 0) {
        //return headerheight;
    }
    return 0;
}

このアプローチは、データソースにもほとんど影響しません。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    int real_section = (int)indexPath.section / 2;
    //your code
}

他のアプローチと比較して、この方法は、tableviewのフレームまたはcontentInsetsを変更しないで安全です。これが役立つことを願っています。

5
Lane

必要なものを取得する最も簡単な方法は、テーブルスタイルをUITableViewStyleGroupedに、セパレータスタイルをUITableViewCellSeparatorStyleNoneに設定することです。

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return CGFLOAT_MIN; // return 0.01f; would work same 
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
    return [[UIView alloc] initWithFrame:CGRectZero];
}

フッタービューをnilとして返さないでください。必要なものを取得する必要がある場合は、ヘッダーの高さとヘッダービューの設定を忘れないでください。

これはあなたの問題を解決しないかもしれませんが、私が同じようなことをしたかったときに私のためにそれをしました。ヘッダーを設定する代わりに、上記のセクションのフッターを使用しました。私を救ったのは、このセクションが本質的に小さく静的であるため、ビューの下部の下にスクロールしないことでした。

4
Torbjörn

For Swift 3 +

単にUITableViewStyleGroupedを使用し、次のようにしてフッターの高さをゼロに設定します。

override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
    return .leastNormalMagnitude
}
3
Tamás Sengel

この問題に対処する方法を考えている間、UITableViewStyleGroupedに関する非常に重要な詳細を思い出しました。 UITableViewがグループ化されたスタイル(セルの周りの丸い境界線)を実装する方法は、UITableViewではなくUITableViewCellsにカスタムbackgroundViewを追加することです。各セルには、セクション内の位置に応じてbackgroundViewが追加されます(上の行はセクションの境界の上部、中央の行はサイドの境界、下のセルは下の部分を取得します)。したがって、単純なスタイルが必要で、セルのカスタムbackgroundViewがない場合(90%の場合)、UITableViewStyleGroupedを使用して、カスタム背景を削除するだけです。 。これは、次の2つの手順に従うことで実行できます。

TableViewスタイルをUITableViewStyleGroupedに変更します。セルを返す直前に、cellForRowに次の行を追加します。

cell.backgroundView = [[[UIView alloc] initWithFrame:cell.bounds] autorelease];

以上です。 tableViewスタイルは、フローティングヘッダーを除き、UITableViewStylePlainとまったく同じになります。

お役に立てれば!

3

上に1つのセクション(ゼロ行)を追加し、上記のsectionFooterViewを現在のセクションのheaderViewとして設定できます。footerViewはフロートしません。それが助けになることを願っています。

1
Lee Lam

たぶん、tableViewでscrollViewDidScrollを使用し、現在の表示ヘッダーに基づいてcontentInsetを変更できます。

私のユースケースでうまくいくようです!

extension ViewController : UIScrollViewDelegate{

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard   let tableView = scrollView as? UITableView,
            let visible = tableView.indexPathsForVisibleRows,
            let first = visible.first else {
            return
        }

        let headerHeight = tableView.rectForHeader(inSection: first.section).size.height
        let offset =  max(min(0, -tableView.contentOffset.y), -headerHeight)
        self.tableView.contentInset = UIEdgeInsetsMake(offset, 0, -offset, 0)
   }
}
1
Martin

別の方法は、ヘッダーを配置するセクションの直前に空のセクションを作成し、そのセクションにヘッダーを配置することです。セクションが空なので、ヘッダーはすぐにスクロールします。

1
devguydavid

@samvermetteのソリューションのバリエーション:

/// Allows for disabling scrolling headers in plain-styled tableviews
extension UITableView {

    static let shouldScrollSectionHeadersDummyViewHeight = CGFloat(40)

    var shouldScrollSectionHeaders: Bool {
        set {
            if newValue {
                tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: bounds.size.width, height: UITableView.shouldScrollSectionHeadersDummyViewHeight))
                contentInset = UIEdgeInsets(top: -UITableView.shouldScrollSectionHeadersDummyViewHeight, left: 0, bottom: 0, right: 0)
            } else {
                tableHeaderView = nil
                contentInset = .zero
            }
        }

        get {
            return tableHeaderView != nil && contentInset.top == UITableView.shouldScrollSectionHeadersDummyViewHeight
        }
    }

}
0
raf

StoryBoardでヘッダーを実装する方法の答えを確認してください。 StoryBoardsのテーブルヘッダービュー

また、実装しないと

viewForHeaderInSection:(NSInteger)section

それはあなたが望んでいるものとまったく同じです。

0

XAKを無視します。アプリがAppleに受け入れられるようにする場合は、プライベートな方法を検討しないでください。

Interface Builderを使用している場合、これが最も簡単です。ビューの上部(画像が表示される場所)にUIViewを追加し、その下にテーブルビューを追加します。 IBはそれに応じてサイズを調整する必要があります。つまり、テーブルビューの上部が追加したばかりのUIViewの下部に接触し、その高さが画面の残りの部分を覆います。

ここでの考え方は、そのUIViewが実際にはテーブルビューの一部ではない場合、テーブルビューでスクロールしないということです。つまり、tableviewヘッダーを無視します。

インターフェイスビルダーを使用していない場合は、TableViewの位置と高さを正しく取得する必要があるため、もう少し複雑です。

0
imnk

フローティングセクションのヘッダーセクションを完全に削除するには、次のようにします。

- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    return [[UIView alloc] init];
}

return nilは機能しません。

フローティングを無効にしながらセクションヘッダーを表示するには、独自の動作を備えたカスタムビューを提供できます。

0
djskinner