Нужна помощь для продвижения представления в UIScrollView / UIPageControl на раскадровке - PullRequest
1 голос
/ 03 января 2012

Я пытаюсь реализовать UIScrollView с UIPageControl на раскадровке для отображения нескольких представлений. Все примеры, учебники или информация, которые я нашел, используют xib, а не раскадровку. Я пытался адаптировать пример кода Apple, но мне чего-то не хватает. Как видите, все довольно просто.

enter image description here

Однако либо я получаю сообщение об ошибке в ContentController.m - [NSNull view]: нераспознанный селектор отправляется экземпляру 0x129acd8 при тестировании if (controller .view.superview == ноль) где я нажимаю на представление (см. ниже после первой половины кода).

Или работают только UIScrollView и UIPageControl, и я не могу выдвинуть представление в UIScrollView:

enter image description here

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

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

Заранее спасибо

AppDelegate.h

// Nothing special as I don't want to manage it through application delegate
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

AppDelegate.m

// Nothing special as I don't want to manage it through application delegate
#import "AppDelegate.h"

@implementation AppDelegate

@synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application { }

- (void)applicationDidEnterBackground:(UIApplication *)application { }

- (void)applicationWillEnterForeground:(UIApplication *)application { }

- (void)applicationDidBecomeActive:(UIApplication *)application { }

- (void)applicationWillTerminate:(UIApplication *)application { }

@end

ContentController.h

// From Apple's PageControl sample code
#import <UIKit/UIKit.h>

@interface ContentController : UIViewController <UIScrollViewDelegate> {
    UIScrollView *scrollView;
    UIPageControl *pageControl;
    NSMutableArray *viewControllers;
    BOOL pageControlUsed;
}

@property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
@property (nonatomic, retain) IBOutlet UIPageControl *pageControl;
@property (nonatomic, retain) NSMutableArray *viewControllers;

- (IBAction)changePage:(id)sender;

@end

ContentController.m

// From Apple's PageControl sample code

#import "AppDelegate.h"
#import "ContentController.h"
#import "MyViewController.h"

// Private methods
@interface ContentController (PrivateMethods)
- (void)loadScrollViewWithPage:(int)page;
- (void)scrollViewDidScroll:(UIScrollView *)sender;
@end

@implementation ContentController

@synthesize scrollView, pageControl, viewControllers;

static NSUInteger kNumberOfPages = 6;

- (void)viewDidLoad {
    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.
    self.navigationController.navigationBar.hidden=YES;

    // view controllers are created lazily in the meantime, load the array with
    // placeholders which will be replaced on demand
    NSMutableArray *controllers = [[NSMutableArray alloc] init];
    for (unsigned i = 0; i < kNumberOfPages; i++) {
        [controllers addObject:[NSNull null]];
    }
    self.viewControllers = controllers;

    // a page is the width of the scroll view
    scrollView.pagingEnabled = YES;
    scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, scrollView.frame.size.height);
    scrollView.showsHorizontalScrollIndicator = NO;
    scrollView.showsVerticalScrollIndicator = NO;
    scrollView.scrollsToTop = NO;
    scrollView.delegate = self;

    pageControl.numberOfPages = kNumberOfPages;
    pageControl.currentPage = 0;

    // pages are created on demand
    // load the visible page
    // load the page on either side to avoid flashes when the user starts scrolling
    [self loadScrollViewWithPage:0];
    [self loadScrollViewWithPage:1];
}

- (void)viewDidUnload {
    [super viewDidUnload];

    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
    self.pageControl=nil;
    self.pageControl = nil;
    self.viewControllers = nil;
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}

- (void)loadScrollViewWithPage:(int)page {
    if (page < 0)
        return;
    if (page >= kNumberOfPages)
        return;

    // replace the placeholder if necessary
    MyViewController *controller = [viewControllers objectAtIndex:page];

    if ((NSNull *)controller == [NSNull null]) // <== *** HERE SEEMS TO BE THE ERROR ***
    {
        controller = [[MyViewController alloc] initWithPageNumber:page
                                                  initWithNibName:nil
                                                           bundle:nil];

        [viewControllers replaceObjectAtIndex:page withObject:controller];
    }

    // add the controller's view to the scroll view
    if (controller.view.superview == nil)
    {
        CGRect frame = scrollView.frame;
        frame.origin.x = frame.size.width * page;
        frame.origin.y = 0;
        controller.view.frame = frame;
        [scrollView addSubview:controller.view];
    }
}

- (void)scrollViewDidScroll:(UIScrollView *)sender {
    // We don't want a "feedback loop" between the UIPageControl and the scroll delegate in
    // which a scroll event generated from the user hitting the page control triggers updates from
    // the delegate method. We use a boolean to disable the delegate logic when the page control is used.
    if (pageControlUsed) {
        // do nothing - the scroll was initiated from the page control, not the user dragging
        return;
    }

    // Switch the indicator when more than 50% of the previous/next page is visible
    CGFloat pageWidth = scrollView.frame.size.width;
    int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
    pageControl.currentPage = page;

    // load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
    [self loadScrollViewWithPage:page - 1];
    [self loadScrollViewWithPage:page];
    [self loadScrollViewWithPage:page + 1];

    // A possible optimization would be to unload the views+controllers which are no longer visible
}

// At the begin of scroll dragging, reset the boolean used when scrolls originate from the UIPageControl
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    pageControlUsed = NO;
}

- (IBAction)changePage:(id)sender {
    int page = pageControl.currentPage;

    // load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
    [self loadScrollViewWithPage:page - 1];
    [self loadScrollViewWithPage:page];
    [self loadScrollViewWithPage:page + 1];

    // update the scroll view to the appropriate page
    CGRect frame = scrollView.frame;
    frame.origin.x = frame.size.width * page;
    frame.origin.y = 0;
    [scrollView scrollRectToVisible:frame animated:YES];

    // Set the boolean used when scrolls originate from the UIPageControl. See scrollViewDidScroll: above.
    pageControlUsed = YES;
}

@end

MyViewController.h

#import <UIKit/UIKit.h>

@interface MyViewController : UIViewController {
    UILabel *pageNumberLabel;
    int pageNumber;
}

@property (nonatomic, retain) IBOutlet UILabel *pageNumberLabel;

- (id)initWithPageNumber:(int)page initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;

@end

MyViewController.m

#import "MyViewController.h"

@implementation MyViewController

@synthesize pageNumberLabel;

- (void)loadView {
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // Set the label and background color when the view has finished loading
    pageNumberLabel.text = [NSString stringWithFormat:@"Page %d", pageNumber + 1];
}

- (void)viewDidUnload {
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

// Load the view nib and initialize the pageNumber ivar
// classic initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
// class adapted to init the page number
// /!\ be careful, I'm not sure it's working properly
- (id)initWithPageNumber:(int)page initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    pageNumber = page;
    NSLog(@"pageNumber = %i", pageNumber);
}

- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}

@end

Ответы [ 4 ]

1 голос
/ 04 января 2012

Нераспознанная ошибка селектора происходит из-за того, что вы перераспределяете локальную переменную «controller» в операторе if в loadScrollViewWithPage: поскольку вы делаете это, новая переменная контроллера существует только в области действия оператора if и при выходе В операторе if переменная контроллера вне этой области все еще является NSNull.

Это можно исправить, удалив определение переменной внутри оператора if:

if ((NSNull *)controller == [NSNull null])
{
    controller = [self.storyboard instantiateViewControllerWithIdentifier:@"MVC"];
    [controller initWithPageNumber:page];

    [viewControllers replaceObjectAtIndex:page withObject:controller];
}
0 голосов
/ 02 ноября 2016

У меня была такая же проблема с вами, когда я пытался следовать примеру кода UIPageController из Apple Documents.Я сделал мой MyViewController программно, поэтому я напрямую использую следующий код

if ((NSNull *)controller == [NSNull null])
{
  MyViewController * controller = [[MyViewController alloc]init];
  [viewControllers replaceObjectAtIndex:page withObject:controller];
}

И я ничего не изменил в MyViewController.

0 голосов
/ 15 июня 2012

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

Я сделал все почти так же, как вы, за исключением контроллера контента, я поставил следующий код(основываясь на ответе @ jonkroll):

    if ((NSNull *)controller == [NSNull null]){
    controller = [[self.storyboard instantiateViewControllerWithIdentifier:@"MyViewController"] initWithPageNumber:page initWithNibName:nil bundle:nil];
    [viewControllers replaceObjectAtIndex:page withObject:controller];
}

MyViewController (в моем случае это другое имя, но оно неактуально) наследует от UITableViewController, потому что это было то, что мне было нужно.Мой метод initWithPageNumber выглядит следующим образом:

    - (id)initWithPageNumber:(int)page initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
pageNumber = page;
NSLog(@"PageNumber = %i", pageNumber);
return self;

}

Добавлен return.self, обратите внимание, что.

И наконец, я встроил ContentController в контроллер навигации,через редактор-> встроить-> контроллер навигации.

Это почти все.Позже мне даже удалось перейти от MyViewController к другому представлению, и все работает отлично.

Если вам нужна дополнительная помощь, вы можете спросить здесь.

С уважением.

0 голосов
/ 28 января 2012

Итак, у меня была точно такая же проблема, как и у вас, но я только что сделал что-то, что могло бы работать просто отлично.

Краткий ответ - это дополнение к ответу Jonkroll об инициализации контроллера внутри оператора if.,По какой-то причине я не могу объяснить, что пустой заполнитель не равен нулю, поэтому оператор if не выполняется, как вы указали в своем коде.Так что я сделал, изменил if и добавил else к тому же if и загрузил ViewController из раскадровки.

if ((NSNull *)controller == [NSNull null]) {
        controller = [self.storyboard instantiateViewControllerWithIdentifier:@"MVC"];        
        [viewControllers replaceObjectAtIndex:page withObject:[controller initWithPageNumber:page]];
}
else {
        controller = [self.storyboard instantiateViewControllerWithIdentifier:@"MVC"];        
        [viewControllers replaceObjectAtIndex:page withObject:[controller initWithPageNumber:page]];
}

Остальная часть моего кода точно такая же, как ваша.Я знаю, что это не самый лучший код, и я мог бы быть в порядке, просто запустив ViewController без оператора if / else, но у меня нет ответа на вопрос, зачем это нужно, или наихудший, почему на самом деле не работает так, как я ожидаю,Если кто-нибудь знает, пожалуйста, поделитесь.

...