UIRefreshControl без UITableViewController

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

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


person Keller    schedule 19.09.2012    source источник
comment
@DaveDeLong: Нет, Дэйв, что ему делать? Позже на этой странице вы скажете, что его решение не поддерживается, так какое же решение правильное?   -  person matt    schedule 12.11.2012
comment
@matt, он должен использовать UITableViewController и отправить API запроса ошибки, чтобы напрямую использовать UIRefreshControl с UITableView.   -  person Dave DeLong    schedule 12.11.2012
comment
UITableViewController имел (и продолжает иметь) слишком много неясных и нишевых ошибок и проблем с нетривиальными иерархиями представлений ... которые все волшебным образом исчезают, когда вы переключаетесь на использование стандартного VC с подпредставлением TableView. Использование UITVC - плохое начало для любого решения, ИМХО.   -  person Adam    schedule 24.10.2013
comment
@Adam. Появляются ли эти ошибки при использовании UITableViewController в качестве дочернего контроллера представления (таким образом, предоставляя доступ к настройке представления, не прибегая к иерархии tableViewControllers)? Я никогда не сталкивался с проблемами при использовании этого способа.   -  person memmons    schedule 23.02.2014
comment
Возможный дубликат Потяните, чтобы обновить UITableView без UITableViewController   -  person James Moore    schedule 11.11.2015


Ответы (12)


На основе догадки и вдохновения 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;
person Keller    schedule 19.09.2012
comment
К вашему сведению, это неподдерживаемое поведение и может измениться в будущем. Единственная гарантия, которую дает Apple, заключается в том, что использование ее в соответствии с предоставленным API (в данном случае -[UITableViewController setRefreshControl:]) будет продолжать работать. - person Dave DeLong; 21.09.2012
comment
В этой реализации кажется, что прямо перед запуском handleRefresh: происходит неожиданное изменение значения contentInsets в представлении прокрутки на долю секунды. Кто-нибудь еще испытывает это или имеет исправление? (да, я знаю, что это не поддерживается!) - person Tim; 21.11.2012
comment
Вам действительно следует просто использовать для этого представление контейнера. Просто установите его класс контроллера представления на свой собственный подкласс UITableViewController, и вы все сделаете правильно. - person Eugene; 24.12.2012
comment
Добавлен ответ, в котором нет проблемы с заиканием. - person Piotr Tomasik; 02.02.2013
comment
В iOS7 (неизвестно для iOS6) отредактированная версия работает нормально, за исключением случаев, когда вы закрываете и снова открываете приложение. Анимация не воспроизводится до второго обновления. При первом обновлении после повторного открытия приложения это просто полный круг вместо увеличивающегося круга в зависимости от того, как далеко вы его потянули. Есть исправление? - person david2391; 30.11.2013
comment
Прошло много времени с тех пор, как этот ответ был опубликован и принят. Как это решение работает для всех до сих пор? - person Aloha Silver; 28.01.2014
comment
Я все еще получаю небольшое «заикание» в моем представлении таблицы, используя решение «редактировать» в этом ответе. Это не так плохо, как при использовании addSubView, но все же довольно заметно. - person rbertsch8; 13.02.2014
comment
этот ответ - гораздо более элегантное решение, и вы все равно можете использовать построитель интерфейса, и вы не делаете ничего хитрого с точки зрения неподдерживаемое поведение - person Fonix; 18.02.2014
comment
Чтобы избежать упомянутого выше шлепка, при этом все еще обходя UITableViewController, просто добавьте элемент управления обновлением под табличным представлением, например: [_tableView insertSubview:_refreshControl atIndex:0];. Протестировано как с iOS 7, так и с 8;) - person ptitvinou; 25.10.2014
comment
@ptitvinou не могли бы вы рассказать подробнее? Я могу быть глупым с этими элементами управления, но я не могу заставить его работать без "прыжка" или "шлюха", это все равно не выглядит хорошо - person Gyfis; 02.02.2015
comment
@ptitvinou спасибо за быстрый ответ! Похоже, моя проблема не в этой шлюхе. Когда я добавляю UIRefreshControl в uitableview, он «прыгает», когда попадает в смещение «обновить». Когда я добавляю его в UITableViewController, он тоже прыгает. Вы тоже сталкивались с этой проблемой? Я не смог найти для себя рабочего решения (для iOS 7,8) ни в одном из потоков uirefreshcontrol x uitableview ... - person Gyfis; 02.02.2015
comment
Что касается проблемы с прыжком, у меня есть небольшая проблема, когда достигается смещение обновления. Я должен признать, что не знаю почему, и меня это не беспокоит, но если вы уже пробовали использовать UITableViewController (чего я не делал), и это тоже происходит, я бы сказал, что это ограничение компонента. Не стесняйтесь поделиться решением, если найдете его :) - person ptitvinou; 02.02.2015
comment
В итоге я использовал сторонний класс SVPullToRefresh (github.com/samvermette/SVPullToRefresh), в котором он тоже проблемы (= с попыткой скрыть панель поиска на UITableView с contentSize ниже высоты tableView ....), но без текста сама стрелка дает хороший эффект, и, по крайней мере, у нее нет «неразрешимых внутренних» проблем, таких как по умолчанию UIRefreshControl :) - person Gyfis; 03.02.2015
comment
Я решил проблему с заиканием, удалив tableView.estimatedRowHeight - person Tikhonov Alexander; 30.10.2015
comment
У меня работает базовая версия, за исключением того, что элемент управления обновлением выровнен справа от центра. Не знаю, как это исправить. - person Alper; 03.05.2016
comment
@alper, вы уверены, что ваше табличное представление имеет соответствующие ограничения автоматической компоновки, связанные с вашим представлением (в частности, ведущие и конечные)? - person Keller; 06.05.2016
comment
Ах, вот оно что. Вся моя таблица растягивалась прямо за пределы экрана, и мне потребовалось некоторое время, чтобы понять это (инспектор пользовательского интерфейса на самом деле этого не показывает). - person Alper; 09.05.2016
comment
Просто возникла небольшая проблема, и это может быть полезно. Не забудьте установить для свойства tableView bounces значение YES;) - person joao.arruda; 24.06.2016

Чтобы устранить заикание, вызванное принятым ответом, вы можете присвоить свой UITableView 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];
}
person Piotr Tomasik    schedule 03.01.2013
comment
Вам даже не нужно создавать новый UITableView. Просто произнесите initWithStyle: existingTableView.style, а затем выполните newTableViewController.tableView = existingTableView и назначьте refreshControl. Сводится к трем строкам. - person Trenskow; 28.02.2013
comment
Не забудьте вызвать [_tablewViewController didMoveToParentViewController:self]; после добавления подпредставления. - person qix; 11.07.2013
comment
self.tableView = _tableViewController.tableView; должно быть _tableViewController.tableView = self.tableView - person Ali; 17.08.2013
comment
@Linus, зачем это нужно? Я добавил _tableViewController.view в качестве subView в свой собственный метод viewController viewDidLoad, и, похоже, это помогло. Мне даже не нужно было устанавливать _tableViewController.tableView - person taber; 27.01.2014
comment
Причина в том, что я не могу использовать UITableViewController и использую UIViewController с табличным представлением внутри. stackoverflow.com/questions/18900428 / - person lostintranslation; 09.10.2014
comment
@lostintranslation, вы можете абсолютно использовать то, что у меня здесь, в UIViewController, приведенный выше пример на самом деле находится внутри функции UIViewController ViewDidLoad. - person Piotr Tomasik; 10.10.2014
comment
Это решение почти готово, но для меня в момент запуска обновления tableView прыгает примерно на 40 пикселей вниз по экрану. - person Ben Clayton; 21.08.2015
comment
Все еще наблюдаю заикание с iOS 9 ... у кого-нибудь еще это работает? - person ArdenDev; 02.05.2016

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

person Tomohisa Takaoka    schedule 25.10.2012
comment
Точнее, самым чистым способом кажется добавление дочернего контроллера представления типа UITableViewController. - person Zdenek; 19.01.2013
comment
Затем вы можете получить UITableViewController, используя self.childViewControllers.firstObject. - person cbh2000; 17.03.2015
comment
^ вы также можете рассмотреть возможность использования prepareForSegue для получения ссылки на объект UITableViewController в представлении контейнера, поскольку это немедленно запускается как событие перехода при создании экземпляра из раскадровки. - person crarho; 12.01.2017

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

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

ODRefreshControl

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

SlimeRefresh

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

person DrummerB    schedule 19.09.2012
comment
Спасибо за ответ, но это не совсем то, что я искал. Однако первое предложение вашего поста вдохновило меня на то, чтобы попробовать, что же в итоге оказалось решением! - person Keller; 20.09.2012
comment
ODRefreshControl потрясающий - спасибо за подсказку! Очень просто и выполняет свою работу. И, конечно же, гораздо более гибкие, чем у Apple. Я никогда не понимал, почему кто-то будет использовать UITableViewController, ИМО, худший класс во всем API. Он (почти) ничего не делает, но делает ваши взгляды негибкими. - person n13; 17.04.2013
comment
На самом деле это зависит от вашего проекта, что вы там используете ... использование uitableview в моем случае увеличивало сложность проекта, и теперь я переключился на uitableviewcontroller, который как бы разделил мой код на два сегмента, я счастлив, у меня есть 2 небольших файла, которые могут использоваться для отладки вместо одного огромного файла .. - person kush; 30.05.2013
comment
@ n13 Здравствуйте! Хорошая причина использовать UITableViewController - это возможность проектировать со статическими ячейками. К сожалению, недоступно в tableView, находящемся в UIViewController. - person Jean Le Moignan; 19.08.2016
comment
@JeanLeMoignan Инструменты и библиотеки iOS быстро меняются, поэтому мой комментарий трехлетней давности больше не действителен. Я переключился на создание всех пользовательских интерфейсов в коде с помощью SnapKit, и, честно говоря, это намного эффективнее, чем нажатие 10 тысяч раз в конструкторе интерфейсов. Также изменить UITableViewController на UIViewController легко и занимает всего секунду, тогда как в IB это большая проблема. - person n13; 24.08.2016
comment
О, и обновить контроль больше не проблема ... больше нет необходимости в сторонних библиотеках. - person n13; 24.08.2016

Попробуйте отложить вызов метода refreshControl -endRefresh на долю секунды после того, как 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];
person boliva    schedule 03.08.2013
comment
Задержки и ожидания - действительно плохой стиль. - person orkoden; 10.01.2014
comment
@orkoden Согласен. Это обходной путь для уже неподдерживаемого использования UIRefreshControl. - person boliva; 10.01.2014
comment
Вы можете намного проще вызвать метод с задержкой. [self performSelector:@selector(endRefreshing) withObject:nil afterDelay:0.01] - person orkoden; 10.01.2014
comment
Конечный эффект точно такой же, и он не делает «взлом» более / менее элегантным. Использовать для этого -performSelector:withObject:afterDelay: или GCD - дело личного вкуса. - person boliva; 11.01.2014

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, прочтите здесь.

person Ashok R    schedule 22.12.2016

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

Вместо этого я встроил 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
    }

    ...
}
person cbh2000    schedule 05.03.2015

Для 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()
}
person stakahop    schedule 17.05.2016

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

Мне пришлось использовать его из-за некоторых проблем с иерархией представлений, которые у меня были: я создавал некоторые функции, которые требовали передачи представлений в разные места в иерархии представлений, что нарушалось при использовании табличного представления UITableViewController b / c 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 и не установит фрейм, который вы, возможно, захотите сделать. Кроме того, в этой реализации нет tableView по умолчанию, предоставляемого вам при создании экземпляра класса, что вы также можете подумать о добавлении. Я не включаю его здесь, потому что это от случая к случаю, и это решение действительно хорошо сочетается с решением Келлера.

person psilencer    schedule 08.01.2014
comment
Вопрос указан без использования подкласса UITableViewController, и этот ответ относится к подклассу UITableViewController. - person Brian; 09.06.2014

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

Вышеупомянутые решения хороши, но 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)
person Ankit Kumar Gupta    schedule 12.12.2016
comment
Лучшее решение на данный момент - person Karthik K M; 03.03.2017

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

person Mark Bridges    schedule 25.09.2013

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

self.refreshControl = [[UIRefreshControl alloc]init];
[self.refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];
person Clint Pick    schedule 14.09.2013
comment
Не согласен. self.refreshControl - это собственность UITableViewController, а не UIViewController. - person Rickster; 23.12.2013
comment
НЕ РАБОТАЕТ, referhControl не определен для uiviewcontroller - person OMGPOP; 26.02.2014