UIRefreshControl без UITableViewController


308

Просто любопытно, так как это сразу не представляется возможным, но есть ли хитрый способ использовать новый UIRefreshControlкласс iOS 6 без использования UITableViewControllerподкласса?

Я часто использую UIViewControllerс UITableViewподвид и соответствовать UITableViewDataSourceи , UITableViewDelegateа не с помощью UITableViewControllerнаповал.


6
@DaveDeLong: Нет, Дейв, что ему делать? Позже на этой странице вы говорите, что его решение не поддерживается, так что же является правильным решением?
— Мэтт

3
@matt он должен использовать a UITableViewControllerи сообщить об ошибке, запрашивая API для непосредственного использования UIRefreshControla UITableView.
— Дэйв Делонг

31
UITableViewController имел (и продолжает иметь) слишком много непонятных и нишевых ошибок и проблем с нетривиальными иерархиями представления ... которые волшебным образом исчезают, когда вы переключаетесь на использование стандартного VC с подпредставлением TableView. «Использование UITVC» - плохое начало для любого решения, ИМХО.
— Адам

@ Adam Эти ошибки обнаруживаются при использовании UITableViewController в качестве дочернего контроллера представления (таким образом, предоставляя доступ к настройке представления без перебора иерархии tableViewControllers)? Я никогда не сталкивался с проблемами при использовании этого способа.
— мемоны

Ответы:


388

По догадкам и на основе вдохновения DrummerB я попытался просто добавить UIRefreshControlэкземпляр в качестве подпредставления к своему UITableView. И это волшебным образом работает!

UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.myTableView addSubview:refreshControl];

Это добавляет UIRefreshControlвыше табличное представление и работает, как и ожидалось, без использования UITableViewController:)


РЕДАКТИРОВАТЬ: Это выше по-прежнему работает, но, как отмечают некоторые, есть небольшое «заикание» при добавлении UIRefreshControl таким образом. Решением этого является создание экземпляра UITableViewController, а затем установка для него ваших UIRefreshControl и UITableView, то есть:

UITableViewController *tableViewController = [[UITableViewController alloc] init];
tableViewController.tableView = self.myTableView;

self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:@selector(getConnections) forControlEvents:UIControlEventValueChanged];
tableViewController.refreshControl = self.refreshControl;

48
К вашему сведению, это неподдерживаемое поведение и может измениться в будущем. Единственная гарантия, которую Apple дает, заключается в том, что использование ее в соответствии с предоставленным API (в данном случае -[UITableViewController setRefreshControl:]) продолжит функционировать.
— Дэйв Делонг,

18
При такой реализации кажется, что прямо перед срабатыванием handleRefresh:происходит неожиданное изменение значения представления прокрутки в contentInsetsтечение доли секунды. Кто-нибудь еще испытал это или исправил это? (да, я знаю, что это в первую очередь не поддерживается!)
— Тим

6
Вы действительно должны просто использовать представление контейнера для этого. Просто установите класс контроллера представления в свой собственный подкласс, UITableViewControllerи вы будете «делать все правильно».
— Евгений

3
В iOS7 (неизвестно для iOS6) отредактированная версия работает нормально, за исключением случаев, когда вы закрываете и снова открываете приложение. Анимация не воспроизводится, пока вы не обновите второй раз. Первое обновление после повторного открытия приложения, это просто полный круг вместо возрастающего круга в зависимости от того, как далеко вы его опускаете. Есть ли исправление?
— david2391

30
Для того, чтобы избежать необходимости в «Слаттер» , как упоминалось выше , в то же время обходя UITableViewController, просто добавить элемент управления обновления под табличном как так: [_tableView insertSubview:_refreshControl atIndex:0];. Протестировано с iOS 7 и 8;)
— ptitvinou

95

Чтобы устранить заикание, вызванное принятым ответом, вы можете присвоить свой UITableViewa UITableViewController.

_tableViewController = [[UITableViewController alloc]initWithStyle:UITableViewStylePlain];
[self addChildViewController:_tableViewController];

_tableViewController.refreshControl = [UIRefreshControl new];
[_tableViewController.refreshControl addTarget:self action:@selector(loadStream) forControlEvents:UIControlEventValueChanged];

_theTableView = _tableViewController.tableView;

РЕДАКТИРОВАТЬ:

Способ добавить UIRefreshControlбез, UITableViewControllerбез заикания и сохранить хорошую анимацию после обновления данных в табличном представлении.

UIRefreshControl *refreshControl = [UIRefreshControl new];
[refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.theTableView addSubview:refreshControl];
[self.theTableView sendSubviewToBack:refreshControl];

Позже при обработке обновленных данных ...

- (void)handleRefresh:(UIRefreshControl *)refreshControl {
    [self.theTableView reloadData];
    [self.theTableView layoutIfNeeded];
    [refreshControl endRefreshing];
}

7
Вам даже не нужно создавать новый UITableView. Просто скажите initWithStyle: существующиеTableView.style - и затем впоследствии выполните newTableViewController.tableView = существующиеTableView и назначьте refreshControl. Сводит это до трех строк.
— Тренскив

Не забудьте позвонить [_tablewViewController didMoveToParentViewController:self];после добавления подпредставления.
— qix

4
self.tableView = _tableViewController.tableView;должно быть_tableViewController.tableView = self.tableView
— Али

@ Линус, почему это необходимо? Я добавил _tableViewController.view как subView в метод viewDidLoad моего пользовательского viewController, и это, похоже, помогло. Мне даже не нужно было устанавливать _tableViewController.tableView
— Табер

Причина, по которой я не могу использовать UITableViewController и использую UIViewController с табличным представлением внутри. stackoverflow.com/questions/18900428/…
— lostintranslation

21

То, что вы попробуете, это использовать представление контейнера внутри ViewController, который вы используете. Вы можете определить чистый подкласс UITableViewController с выделенным табличным представлением и поместить его в ViewController.


2
Точно, самый чистый способ - добавить дочерний контроллер вида типа UITableViewController.
— Зденек

Затем вы можете получить UITableViewController с помощью self.childViewControllers.firstObject.
— cbh2000

^ Вы также можете рассмотреть возможность использования prepareForSegueдля получения ссылки на объект UITableViewController в представлении контейнера, так как это немедленно запускается как событие segue при создании экземпляра из раскадровки.
— Крахо

18

Ну, UIRefreshControl - это подкласс UIView, так что вы можете использовать его самостоятельно. Я не уверен, хотя, как это отрисовывает себя. Рендеринг может просто зависеть от фрейма, но он также может полагаться на UIScrollView или UITableViewController.

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

ODRefreshControl

введите описание изображения здесь

SlimeRefresh

введите описание изображения здесь


1
Спасибо за ваш ответ, но не совсем то, что я искал. Первое предложение вашего поста, однако, вдохновило меня попробовать то, что в итоге оказалось решением!
— Келлер

7
ODRefreshControl потрясающий - спасибо за совет! Очень просто и делает свою работу. И, конечно, гораздо более гибкий, чем Apple. Я никогда не понимал, почему кто-то будет использовать UITableViewController, IMO худший класс во всем API. Он ничего не делает, но делает ваши взгляды негибкими.
— 13

Это на самом деле зависит от вашего проекта, что вы используете там ... использование uitableview в моем случае увеличивало сложность проекта, и теперь я переключился на uitableviewcontroller, который разделил мой код на два сегмента. Я рад, что у меня есть 2 файла поменьше, которые могут использоваться для отладки вместо одного огромного файла ..
— kush

@ n13 Здравствуйте, хорошая причина использовать UITableViewController - возможность создавать статические ячейки. К сожалению, недоступно в tableView, находящемся в UIViewController.
— Жан Ле Мойнан

@JeanLeMoignan Инструменты и библиотеки iOS быстро меняются, поэтому мой трехлетний комментарий больше не действителен. Я перешел на создание всех пользовательских интерфейсов в коде с помощью SnapKit, и, честно говоря, это намного эффективнее, чем нажатие 10 тысяч раз в конструкторе интерфейсов. Кроме того, изменение UITableViewController на UIViewController легко и занимает всего секунду, тогда как в IB это большая боль.
— n13

7

Попробуйте отложить вызов -endRefreshметода refreshControl на долю секунды после того, как tableView перезагрузит свое содержимое, используя NSObject -performSelector:withObject:afterDelay:или GCD dispatch_after.

Я создал категорию на UIRefreshControl для этого:

@implementation UIRefreshControl (Delay)

- (void)endRefreshingAfterDelay:(NSTimeInterval)delay {
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self endRefreshing];
    });
}

@end

Я проверил это, и это также работает на коллекцию просмотров. Я заметил, что задержка в 0,01 секунды достаточно:

// My data refresh process here while the refresh control 'isRefreshing'
[self.tableView reloadData];
[self.refreshControl endRefreshingAfterDelay:.01];

4
Задержки и ожидания действительно плохой стиль.
— orkoden

2
@orkoden Я согласен. Это обходной путь для уже неподдерживаемого использования UIRefreshControl.
— Болива

Вы можете вызвать метод с задержкой намного проще с. [self performSelector:@selector(endRefreshing) withObject:nil afterDelay:0.01]
— orkoden

2
Конечный эффект точно такой же, и не делает «хак» более / менее элегантным. Использовать ли -performSelector:withObject:afterDelay:или GCD для этого - дело личного вкуса.
— Болива

7

IOS 10 Swift 3.0

это просто

import UIKit

class ViewControllerA: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var myTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        myTableView.delegate = self
        myTableView.dataSource = self

        if #available(iOS 10.0, *) {
            let refreshControl = UIRefreshControl()
            let title = NSLocalizedString("PullToRefresh", comment: "Pull to refresh")
            refreshControl.attributedTitle = NSAttributedString(string: title)
            refreshControl.addTarget(self,
                                     action: #selector(refreshOptions(sender:)),
                                     for: .valueChanged)
            myTableView.refreshControl = refreshControl
        }
    }

    @objc private func refreshOptions(sender: UIRefreshControl) {
        // Perform actions to refresh the content
        // ...
        // and then dismiss the control
        sender.endRefreshing()
    }

    // MARK: - Table view data source

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 12
    }


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)

        cell.textLabel?.text = "Cell \(String(indexPath.row))"
        return cell
    }

}

введите описание изображения здесь

Если вы хотите узнать о iOS 10 UIRefreshControl, прочитайте здесь .


3

Добавление элемента управления обновлением в качестве подпредставления создает пустое пространство над заголовками раздела.

Вместо этого я встроил UITableViewController в свой UIViewController, а затем изменил свой tableView свойство, чтобы оно указывало на встроенное, и альт! Минимальные изменения кода. :-)

шаги:

  1. Создайте новый UITableViewController в раскадровке и вставьте его в свой оригинальный UIViewController
  2. Замените @IBOutlet weak var tableView: UITableView!на один из недавно встроенного UITableViewController, как показано ниже

class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let tableViewController = self.childViewControllers.first as! UITableViewController
        tableView = tableViewController.tableView
        tableView.dataSource = self
        tableView.delegate = self

        // Now we can (properly) add the refresh control
        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action: "handleRefresh:", forControlEvents: .ValueChanged)
        tableViewController.refreshControl = refreshControl
    }

    ...
}

2

Для Swift 2.2.

Сначала создайте UIRefreshControl ().

var refreshControl : UIRefreshControl!

В вашем методе viewDidLoad () добавьте:

refreshControl = UIRefreshControl()
    refreshControl.attributedTitle = NSAttributedString(string: "Refreshing..")
    refreshControl.addTarget(self, action: #selector(YourUIViewController.refresh(_:)), forControlEvents: UIControlEvents.ValueChanged)
    self.tableView.addSubview(refreshControl)

И сделать функцию обновления

func refresh(refreshControl: UIRefreshControl) {

    // do something ...

    // reload tableView
    self.tableView.reloadData()

    // End refreshing
    refreshControl.endRefreshing()
}

0

Вот еще одно решение, которое немного отличается.

Мне пришлось использовать его из-за некоторых проблем с иерархией представлений, которые у меня были: я создавал некоторые функциональные возможности, которые требовали передачи представлений в разные места в иерархии представлений, которые ломались при использовании табличного представления UITableViewController, поскольку tableView является корневым представлением UITableViewController ( self.view), а не просто обычное представление, оно создало противоречивые иерархии контроллера / представления и вызвало сбой.

По сути, создайте свой собственный подкласс UITableViewController и переопределите loadView, чтобы назначить self.view другое представление, и переопределите свойство tableView для возврата отдельного представления таблицы.

например:

@interface MyTableVC : UITableViewController
@end

@interface MyTableVC ()
@property (nonatomic, strong) UITableView *separateTableView;
@end

@implementation MyTableVC

- (void)loadView {
    self.view = [[UIView alloc] initWithFrame:CGRectZero];
}

- (UITableView *)tableView {
    return self.separateTableView;
}

- (void)setTableView:(UITableView *)tableView {
    self.separateTableView = tableView;
}

@end

В сочетании с решением Келлера это будет более устойчивым в том смысле, что tableView теперь является обычным представлением, а не корневым представлением VC, и будет более устойчивым к изменению иерархий представлений. Пример использования этого способа:

MyTableVC *tableViewController = [[MyTableVC alloc] init];
tableViewController.tableView = self.myTableView;

self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:@selector(getConnections) forControlEvents:UIControlEventValueChanged];
tableViewController.refreshControl = self.refreshControl;

Существует еще одно возможное использование для этого:

Так как создание подклассов таким способом отделяет self.view от self.tableView, теперь возможно использовать этот UITableViewController в качестве более обычного контроллера и добавлять другие подпредставления в self.view без странностей добавления подпредставлений в UITableView, поэтому можно рассмотреть возможность создания их просматривать контроллеры непосредственно подкласс UITableViewController вместо того, чтобы иметь дочерние UITableViewController.

Некоторые вещи, на которые стоит обратить внимание:

Так как мы переопределяем свойство tableView, не вызывая super, могут быть некоторые вещи, на которые следует обратить внимание, и которые необходимо обработать при необходимости Например, установка tableview в моем примере выше не добавит tableview к self.view и не установит кадр, который вы, возможно, захотите сделать. Кроме того, в этой реализации нет табличного представления по умолчанию, предоставленного вам при создании экземпляра класса, что также можно рассмотреть при добавлении. Я не включаю это здесь, потому что это в каждом конкретном случае, и это решение действительно хорошо согласуется с решением Келлера.


1
Вопрос задан «без использования подкласса UITableViewController», и этот ответ для подкласса UITableViewController.
— Брайан

0

Попробуй это,

Вышеуказанные решения хороши, но tableView.refreshControl доступен только для UITableViewController до iOS 9.x и поставляется в UITableView начиная с iOS 10.x и далее.

Написано в Swift 3 -

let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(FeedViewController.loadNewData), for: UIControlEvents.valueChanged)
// Fix for the RefreshControl Not appearing in background
tableView?.addSubview(refreshControl)
tableView.sendSubview(toBack: refreshControl)

Лучшее решение на данный момент
— Karthik KM

-1

Первое предложение Келлера вызывает странную ошибку в iOS 7, когда вкладка таблицы увеличивается после появления контроллера представления. Переход ко второму ответу с помощью контроллера uitableview позволил мне все исправить.


-6

Оказывается, вы можете использовать следующее при использовании UIViewController с подпредставлением UITableView и соответствуйте UITableViewDataSource и UITableViewDelegate:

self.refreshControl = [[UIRefreshControl alloc]init];
[self.refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];

9
Не согласен. self.refreshControlэто свойство для, а UITableViewControllerне UIViewController.
— Рикстер

НЕ РАБОТАЕТ, referhControl не определен для uiviewcontroller
— OMGPOP
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.