Блокировка UISearchBar в верхней части UITableView, как Game Center - PullRequest
27 голосов
/ 02 февраля 2011

Эта классная функция есть в UITableViews в Game Center и на панелях поиска, которые они имеют на своих верхах.В отличие от приложений, в которых панель поиска находится в представлении заголовка таблицы (поэтому она считается стандартной ячейкой таблицы), вместо этого она, кажется, прикреплена к родительской панели навигации над ней.Таким образом, при прокрутке таблицы панель поиска действительно перемещается, но если вы прокручиваете выше границ таблицы, панель поиска никогда не перестает касаться панели навигации.

Кто-нибудь знает, как это могло быть сделано?Мне было интересно, может ли Apple поместить и строку поиска, и таблицу в родительское представление прокрутки, но мне интересно, может ли это быть проще, чем это.

Ответы [ 7 ]

28 голосов
/ 13 марта 2011

Ответ Боба перевернут: он должен быть MIN (0, scrollView.contentOffset.y).

Кроме того, для правильной поддержки изменения размера (которое будет происходить при повороте), другие значения кадра должныбыть повторно использованы.

-(void)scrollViewDidScroll:(UIScrollView *)scrollView 
{
    UISearchBar *searchBar = searchDisplayController.searchBar;
    CGRect rect = searchBar.frame;
    rect.origin.y = MIN(0, scrollView.contentOffset.y);
    searchBar.frame = rect;
}
7 голосов
/ 03 февраля 2011

Вы можете поместить searchBar в заголовок таблицы и реализовать метод делегирования - (void) scrollViewDidScroll: (UIScrollView *) для tableView.Выполнение чего-то подобного должно работать:

-(void) scrollViewDidScroll:(UIScrollView *)scrollView {
    searchBar.frame = CGRectMake(0,MAX(0,scrollView.contentOffset.y),320,44);
} 

Если вы используете searchDisplayController, вы получите доступ к панели поиска с помощью self.searchDisplayController.searchbar.

5 голосов
/ 22 января 2016

В Swift 2.1 и iOS 9.2.1

    let searchController = UISearchController(searchResultsController: nil)

        override func viewDidLoad() {
        /* Search controller parameters */
            searchController.searchResultsUpdater = self  // This protocol allows your class to be informed as text changes within the UISearchBar.
            searchController.dimsBackgroundDuringPresentation = false  // In this instance,using current view to show the results, so do not want to dim current view.
            definesPresentationContext = true   // ensure that the search bar does not remain on the screen if the user navigates to another view controller while the UISearchController is active.

            let tableHeaderView: UIView = UIView.init(frame: searchController.searchBar.frame)
            tableHeaderView.addSubview(searchController.searchBar)
            self.tableView.tableHeaderView = tableHeaderView

        }
        override func scrollViewDidScroll(scrollView: UIScrollView) {
           let searchBar:UISearchBar = searchController.searchBar
           var searchBarFrame:CGRect = searchBar.frame
           if searchController.active {
              searchBarFrame.origin.y = 10
           }
           else {
             searchBarFrame.origin.y = max(0, scrollView.contentOffset.y + scrollView.contentInset.top)

           }
           searchController.searchBar.frame = searchBarFrame
       }
3 голосов
/ 29 июня 2013

Есть еще один шаг, если вы хотите полностью эмулировать панели поиска в Game Center.

Если вы начнете с превосходного ответа Friedenberg * , а также followben's Модификация для iOS 6+, упомянутая в комментариях, все равно необходимо настроить функциональность, когда активна сама панель поиска.

В Game Center панели поиска будут прокручиваться вместе с таблицей по мере прокрутки вниз, но будутоставайтесь зафиксированными под панелью навигации, когда вы пытаетесь прокрутить вверх за границы таблицы.Однако, когда панель поиска активна и результаты поиска отображаются, панель поиска больше не прокручивается вместе с таблицей; он остается фиксированным на месте под панелью навигации .

Вот полный код (для iOS 6+) для реализации этого.(Если вы ориентируетесь на iOS 5 или ниже, вам не нужно оборачивать UISearchBar в UIView)

CustomTableViewController.m

- (void)viewDidLoad
{
    UISearchBar *searchBar = [[UISearchBar alloc] init];
    ...
    UIView *tableHeaderView = [[UIView alloc] initWithFrame:searchBar.frame];
    [tableHeaderView addSubview:searchBar];

    [self.tableView setTableHeaderView:tableHeaderView];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    UISearchBar *searchBar = self.tableView.tableHeaderView.subviews.lastObject;
    CGRect searchBarFrame = searchBar.frame;

    /*
     * In your UISearchBarDelegate implementation, set a boolean flag when
     * searchBarTextDidBeginEditing (true) and searchBarTextDidEndEditing (false)
     * are called.
     */

    if (self.inSearchMode)
    {
        searchBarFrame.origin.y = scrollView.contentOffset.y;
    }
    else
    {
        searchBarFrame.origin.y = MIN(0, scrollView.contentOffset.y);
    }

    searchBar.frame = searchBarFrame;
}

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
    self.inSearchMode = YES;
}

- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
    self.inSearchMode = NO;
}

Voilà!Теперь, когда панель поиска неактивна, она будет перемещаться вместе с таблицей и оставаться неизменной при попытке выйти за границы таблицы.Когда он активен, он останется на месте, как в Game Center.

1 голос
/ 28 сентября 2016

В то время как другие ответы кажутся полезными и частично выполняют свою работу, это не решает проблему с тем, что панель поиска не получает прикосновения пользователя, потому что она перемещается за пределы родительского представления при изменении его фрейма.* Хуже того, когда вы нажимаете на панель поиска, чтобы сделать ее первым респондентом, весьма вероятно, что делегат tableView вызовет tableView:didSelectRowAtIndexPath: в ячейке, расположенной под панелью поиска.

Для решения этих проблем, описанных выше, вам нужно обернуть панель поиска в простой UIView, вид, который способен обрабатывать касания, произошедшие за пределами его границ.Таким образом, вы можете передать эти прикосновения к панели поиска.

Итак, давайте сделаем это сначала:

class SearchBarView: UIView {
  override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
    for subview in subviews {
      if !subview.userInteractionEnabled { continue }

      let newPoint = subview.convertPoint(point, fromView: self)
      if CGRectContainsPoint(subview.bounds, newPoint) {
        return subview.hitTest(newPoint, withEvent: event)
      }
    }
    return super.hitTest(point, withEvent: event)
  }
}

Прямо сейчас у нас есть подкласс UIView с именем SearchBarView, который способен принимать касания, произошедшие за пределами его границ.

Во-вторых, мы должны поместить панель поиска в это новое представление, пока контроллер представления загружает его:

class TableViewController: UITableViewController {
  private let searchBar = UISearchBar(frame: CGRectZero)
  ...

  override func viewDidLoad() {
    super.viewDidLoad()
    ...
    searchBar.sizeToFit()
    let searchBarView = SearchBarView(frame: searchBar.bounds)
    searchBarView.addSubview(searchBar)

    tableView.tableHeaderView = searchBarView
  }
}

Наконец, мы должны обновить фрейм панели поиска как пользовательпрокручивает представление таблицы вниз, чтобы оно оставалось фиксированным сверху:

override func scrollViewDidScroll(scrollView: UIScrollView) {
  searchBar.frame.origin.y = max(0, scrollView.contentOffset.y)
}

Вот результат:

enter image description here

-

Важное примечание: если в вашем табличном представлении есть разделы, они, вероятно, будут затенять вашу панель поиска, поэтому вам нужно ставить панель поиска поверх них при каждом обновлении bounds табличного представления.

enter image description here

viewDidLayoutSubviews - хорошее место для этого:

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()
  ...
  if let tableHeaderView = tableView.tableHeaderView {
    tableView.bringSubviewToFront(tableHeaderView)
  }
}

-

Надеждаэто помогает.Вы можете скачать пример проекта с здесь .

0 голосов
/ 17 февраля 2017

Если ваша цель развертывания - iOS 9 и выше, вы можете использовать привязки и программно установить UISearchBar и UITableView:

    private let tableView = UITableView(frame: .zero, style: .plain)
    private let searchBar = UISearchBar(frame: CGRect .zero)

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.delegate = self
        tableView.dataSource = self
        tableView.contentInset = UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0)

        searchBar.delegate = self
        view.addSubview(tableView)
        view.addSubview(searchBar)
        NSLayoutConstraint.activate([
            searchBar.heightAnchor.constraint(equalToConstant: 44.0),
            searchBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            searchBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            searchBar.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor)
            ])
    }

Я предполагаю, что вы создаете UISearchBar и UITableView из кода, а не в раскадровке.

0 голосов
/ 11 июня 2014

Все остальные ответы предоставили мне полезную информацию, но ни один из них не работал на iOS 7.1.Вот упрощенная версия того, что мне помогло:

MyViewController.h:

@interface MyViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UISearchDisplayDelegate> {
}
@end

MyViewController.m:

@implementation MyViewController {

    UITableView *tableView;
    UISearchDisplayController *searchDisplayController;
    BOOL isSearching;
}

-(void)viewDidLoad {
    [super viewDidLoad];

    UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
    searchBar.delegate = self;

    searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
    searchDisplayController.delegate = self;
    searchDisplayController.searchResultsDataSource = self;
    searchDisplayController.searchResultsDelegate = self;

    UIView *tableHeaderView = [[UIView alloc] initWithFrame:searchDisplayController.searchBar.frame];
    [tableHeaderView addSubview:searchDisplayController.searchBar];
    [tableView setTableHeaderView:tableHeaderView];

    isSearching = NO;
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView {

    UISearchBar *searchBar = searchDisplayController.searchBar;
    CGRect searchBarFrame = searchBar.frame;

    if (isSearching) {
        searchBarFrame.origin.y = 0;
    } else {
        searchBarFrame.origin.y = MAX(0, scrollView.contentOffset.y + scrollView.contentInset.top);
    }

    searchDisplayController.searchBar.frame = searchBarFrame;
}

- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
    isSearching = YES;
}

-(void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
    isSearching = NO;
}

@end

Примечание.нажмите «Обновить» в вашем списке, вам нужно заменить scrollView.contentInset.top в scrollViewDidScroll: на константу, чтобы панель поиска могла прокручивать анимацию обновления.

...