Вопрос по ios – IOS: ActivityIndicator через UITableView ... Как?

18

Я хочу отображать индикатор активности поверх uitableview, пока он загружает данные (в другом потоке). Итак, в методе ViewDidLoad UITableViewController:

-(void)viewDidLoad
 {
    [super viewDidLoad];

    //I create the activity indicator
    UIActivityIndicatorView *ac = [[UIActivityIndicatorView alloc]  
                      initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    [ac startAnimating];

    //Create the queue to download data from internet
    dispatch_queue_t downloadQueue = dispatch_queue_create("PhotoDownload",NULL); 
    dispatch_async(downloadQueue, ^{
      //Download photo
      .......
      .......
      dispatch_async(dispatch_get_main_queue(), ^{
         ......
         ......
         [ac stopAnimating];
      });
    });
   .......

Почему индикатор активности не отображается над представлением таблицы? Как я могу этого достичь?

Также интересует этот вопрос. Но, насколько я знаю, подклассы UIView не являются поточно-ориентированными. Maxim Mikheev

Ваш Ответ

5   ответов
12

но я не получил то, что хотел. Поняв, как работает индикатор активности и контроллер табличного представления, я пришел к следующему решению.

По какой-то причине, если вы пытаетесь запустить индикатор активности в том же потоке с tableReload или любым другим дорогим процессом, индикатор активности никогда не запускается. Если вы попытаетесь запустить перезагрузку таблицы или какую-либо другую операцию в другом потоке, это может быть небезопасно и может привести к ошибке или нежелательным результатам. Таким образом, мы можем запустить метод, который представляет индикатор активности в другом потоке.

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

-(void)showActivityViewer
{
    AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
    UIWindow *window = delegate.window;
    activityView = [[UIView alloc] initWithFrame: CGRectMake(0, 0, window.bounds.size.width, window.bounds.size.height)];
    activityView.backgroundColor = [UIColor blackColor];
    activityView.alpha = 0.5;

    UIActivityIndicatorView *activityWheel = [[UIActivityIndicatorView alloc] initWithFrame: CGRectMake(window.bounds.size.width / 2 - 12, window.bounds.size.height / 2 - 12, 24, 24)];
    activityWheel.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhite;
    activityWheel.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |
                                      UIViewAutoresizingFlexibleRightMargin |
                                      UIViewAutoresizingFlexibleTopMargin |
                                      UIViewAutoresizingFlexibleBottomMargin);
    [activityView addSubview:activityWheel];
    [window addSubview: activityView];

    [[[activityView subviews] objectAtIndex:0] startAnimating];
}

-(void)hideActivityViewer
{
    [[[activityView subviews] objectAtIndex:0] stopAnimating];
    [activityView removeFromSuperview];
    activityView = nil;
}

- (IBAction)reloadDataAction:(id)sender {
    [NSThread detachNewThreadSelector:@selector(showActivityViewer) toTarget:self withObject:nil];  
    //... do your reload or expensive operations
    [self hideActivityViewer];
}
Создайте категорию с указанными выше методами внутри нее
Я долго искал, и я должен сказать, что это лучшее решение на данный момент.
Это работало довольно хорошо для меня, после нескольких альтернатив. Спасибо @zirinisp!
Это работает, но как я могу переместить это в вспомогательный класс, чтобы я мог вызвать любое представление?
Это кажется чрезвычайно опасным. Работа с пользовательским интерфейсом должна происходить в основном потоке (то есть добавлении и удалении подпредставления как минимум). Кроме того, учитывая, что showActivityViewer и hideActivityViewer работают в двух разных потоках, может произойти любое количество условий гонки (например, hideActivityViewer может быть вызвано до того, как окно addSubview и весь ад выпадет).
21

ть его в представление заголовка UITableView. Для этого вам нужно будет предоставить свой собственный вид.

...
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
[view addSubview:ac]; // <-- Your UIActivityIndicatorView
self.tableView.tableHeaderView = view;
...
и где AC живет, прежде чем добавить его во вновь созданный вид?
Избей меня до удара! +1 за ответ. Индикатор активности - это UIView, поэтому его необходимо каким-то образом добавить в иерархию представлений!
6


Если вы просто хотите отобразить ActivityWheel без дополнительного родительского представления, вы также можете добавить ActivityWheel прямо в tableView и вычислить значение y для фрейма, используя tableViews contentOffset:

@interface MyTableViewController ()
@property (nonatomic, strong) UIActivityIndicatorView *activityView;
@end

// ....

- (void) showActivityView {
    if (self.activityView==nil) {
        self.activityView = [[UIActivityIndicatorView alloc] initWithFrame:CGRectZero];
        [self.tableView addSubview:self.activityView];
        self.activityView.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
        self.activityView.hidesWhenStopped = YES;
        // additional setup...
        // self.activityView.color = [UIColor redColor]; 
    }
    // Center 
    CGFloat x = UIScreen.mainScreen.applicationFrame.size.width/2;
    CGFloat y = UIScreen.mainScreen.applicationFrame.size.height/2;
    // Offset. If tableView has been scrolled
    CGFloat yOffset = self.tableView.contentOffset.y;
    self.activityView.frame = CGRectMake(x, y + yOffset, 0, 0);

    self.activityView.hidden = NO;
    [self.activityView startAnimating];
}

- (void) hideActivityView {
    [self.activityView stopAnimating];
}

Если ActivityView не появляется сразу (или никогда), обратитесь к ответу zirinisp или выполните тяжелую работу в фоновом режиме.

-1

[IOS 8+] Выше, не работает для меня. Вместо этого я храню представление индикатора (ac) в классе. Тогда я делаю:

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    if (ac.isAnimating)
        return 50.0f;
    return 0.0f;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    if (ac.isAnimating)
        return ac;
    return nil;
}

Для показа индикатора я делаю

[ac startAnimating]

Я скрываю это

[ac stopAnimating]

Я надеюсь, что это кому-то поможет.

4

Есть обходной путь для UIViewController. (Вы можете использовать UIViewController с табличным представлением для достижения UITableViewController.) В раскадровке добавьте ActivityIndicator в представлении UIViewController. Затем в viewDidLoad добавьте следующее:

[self.activityIndicator layer].zPosition = 1;

ActivityIndicator будет отображаться поверх табличного представления.

Похожие вопросы