在 superView 上执行选择器

Performing selector on superView

我有一个 UICollectionView,它使用 - (void)scrollViewDidScroll:(UIScrollView *)scrollView 来确定用户何时滚动到底部。

我的 UICollectionView 的所有者有一种方法可以向服务器发送命令并请求更多数据。

我想做的是从我的 UICollectionView 中对所有者(我的 viewcontroller)执行选择器,特别是在 scrollViewDidScroll.

我尝试使用以下代码来实现:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    CGFloat offsetY = scrollView.contentOffset.y;
    CGFloat contentHeight = scrollView.contentSize.height;
    if (offsetY > contentHeight - scrollView.frame.size.height)
    {
        [[self superView] performSelector:@selector(onScrolledToBottom) withObject:nil];
    }
} 

注意:选择器onScrolledToBottom是我的UICollectionView.

SEL属性

我得到的错误说:

-[UIView onScrolledToBottom]: unrecognized selector sent to instance 0x7fc641e76e00
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIView onScrolledToBottom]: unrecognized selector sent to instance 0x7fc641e76e00'

编辑

我已经精简了我的代码来解决这个问题。

我的 ViewController.m

中有以下内容
....

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self initCollectionView];
}
....

- (void) getMoreInfo{
    NSLog(@"Getting more info");
}

- (void) initCollectionView{
    UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc]init];
    flowLayout.itemSize = CGSizeMake(50.0, 60.0);
    flowLayout.sectionInset = UIEdgeInsetsMake(20, 0, 20, 0);
    [flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
    flowLayout.headerReferenceSize = CGSizeMake(320.f, 30.f);

    myCollectionView *mVC = [myCollectionView alloc] init: self.view: flowLayout: @selector(getMoreInfo)];
}

对于我的 myCollectionView.h.m 文件如下所示:

#import <UIKit/UIKit.h>

@interface myCollectionView : UICollectionView<UIScrollViewDelegate, UICollectionViewDelegate, UICollectionViewDataSource>{
    UIView *parentView;
    SEL onScrolledToBottom;
}

@property UIView *parentView;
@property SEL onScrolledToBottom;

- (id)init: (UIView*) parent: (UICollectionViewLayout *)layout: (SEL)onScrolledToBottomSEL;
@end

#import "myCollectionView.h"

@implementation myCollectionView

@synthesize parentView = parentView;
@synthesize onScrolledToBottom = onScrollToBotton;

- (id)init: (UIView*) parent: (UICollectionViewLayout *)layout: (SEL)onScrolledToBottomSEL{
    self = [super initWithFrame: CGRectMake(0.0f, 0.0f, parent.frame.size.width, parent.frame.size.height) collectionViewLayout: layout];
    if (self) {
        parentView = parent;
        self.delegate = self;
        self.dataSource = self;
        onScrolledToBottom = onScrolledToBottomSEL;
    }

    return self;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    CGFloat offsetY = scrollView.contentOffset.y;
    CGFloat contentHeight = scrollView.contentSize.height;
    if (offsetY > contentHeight - scrollView.frame.size.height)
    {
        [parentView performSelector:@selector(onScrolledToBottom) withObject:nil];
    }
}

collectionView.parentView 将是 ViewController.view,这完全是一个 UIView,没有名为 onScrolledToBottom 的方法。

如果我对你想要的是正确的,这里有一个方法,但是这样做真的很糟糕

ViewController.m

....    

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self initCollectionView];
}
....    

- (void) getMoreInfo{
    NSLog(@"Getting more info");
}    

- (void) initCollectionView{
    UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc]init];
    flowLayout.itemSize = CGSizeMake(50.0, 60.0);
    flowLayout.sectionInset = UIEdgeInsetsMake(20, 0, 20, 0);
    [flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
    flowLayout.headerReferenceSize = CGSizeMake(320.f, 30.f);    

    myCollectionView *mVC = [myCollectionView alloc] initWithParentViewController:self layout:flowLayout selector:@selector(getMoreInfo)];
}

myCollectionView.h

#import <UIKit/UIKit.h>    

@interface myCollectionView : UICollectionView<UIScrollViewDelegate, UICollectionViewDelegate, UICollectionViewDataSource>{
    UIViewController *_parentViewController;
    SEL _onScrolledToBottom;
}    

@property(weak) UIViewController *parentViewController;
@property SEL onScrolledToBottom;    

- (id)initWithParentViewController:(UIView *)parentViewController layout:(UICollectionViewLayout *)layout selector:(SEL)onScrolledToBottomSEL;
@end

myCollectionView.m

#import "myCollectionView.h"    

@implementation myCollectionView    

@synthesize parentViewController = _parentViewController;
@synthesize onScrolledToBottom = _onScrollToBotton;    

- (id)initWithParentViewController:(UIView *)parentViewController layout:(UICollectionViewLayout *)layout selector:(SEL)onScrolledToBottomSEL{
    self = [super initWithFrame: CGRectMake(0.0f, 0.0f, parent.frame.size.width, parent.frame.size.height) collectionViewLayout: layout];
    if (self) {
        _parentViewController = parentViewController;
        self.delegate = self;
        self.dataSource = self;
        _onScrolledToBottom = onScrolledToBottomSEL;
    }    
    return self;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    CGFloat offsetY = scrollView.contentOffset.y;
    CGFloat contentHeight = scrollView.contentSize.height;
    if (offsetY > contentHeight - scrollView.frame.size.height)
    {
        [_parentViewController performSelector:@selector(onScrolledToBottom) withObject:nil];
    }
}

警告:

  • MVC中应该有一个Controller来实现 协议,而不是视图。
  • Class 名称的第一个字符必须大写,您应该使用 MyCollectionView 而不是 myCollectionView 作为 class 名称。
  • 使用属性时,LLVM编译器会自动生成一个成员变量,无需再次声明。
  • 在这种情况下使用 protocol-delegate 是最好的方法。

您获得的是父视图而不是父控制器,仅此而已。其次,你不应该尝试这样做。而是使用协议并将您的控制器作为委托传递给您的自定义集合视图。

@protocol MyCollectionViewScrollProtocol <NSObject>

- (void)scrolledToBottom;

@end

然后在您的 ViewController 中实施此协议并在您的 CollectionView

像这样创建一个弱 属性 委托:

@property(weak, nonatomic) id<MyCollectionViewScrollProtocol> delegate;

那你就可以调用了

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    CGFloat offsetY = scrollView.contentOffset.y;
    CGFloat contentHeight = scrollView.contentSize.height;
    if (offsetY > contentHeight - scrollView.frame.size.height)
    {
        [self.delegate  scrolledToBottom];
    }
}

并且不要忘记从控制器设置委托 属性。


OP编辑:

控制器应在 .h 文件中包含以下代码:

#import <UIKit/UIKit.h>

@protocol ViewControllerProtocol <NSObject>
    - (void) Pong;
@end

@interface ViewController : UIViewController <ViewControllerProtocol>

@end

.m 文件中某处的以下代码:

#import "ViewController.h"
@import UIKit;
#import "myUnit.h"

@interface ViewController ()
@end

@implementation ViewController


- (void)viewDidLoad {
    [super viewDidLoad];
    myUnit mU = [[myUnit alloc] init];
    mU.linkToController = self;
    [mU Ping];
}

......

- (void) Pong{
    NSLog(@"Pong");
}

......

想要访问控制器的单元需要在.h文件中有如下代码:

#import <Foundation/Foundation.h>
#import "ViewController.h"

@interface myUnit : NSObject {
}

@property (weak, nonatomic) id <ViewControllerProtocol> linkToController;

- (void)Ping;

@end

而想要访问控制器的单元需要在.m文件中有如下代码:

#import "myUnit.h"
#import "ViewController.h"


@implementation myUnit

- (void) Ping {
    NSLog(@"Ping");
    [self.linkToController Pong];
}


@end

你的 "myCollectionView" 应该有一个委托(你必须为此创建一个协议),并使用一些方法:

- (void)myCollectionViewDidScrollToBottom:(myCollectionView *)collectionView

按照惯例,视图会将自身传递给此方法(就像任何 tableView 委托方法一样)。然后在 myCollectionView *mVC = [myCollectionView alloc] init... 之后的 initCollectionView 中,您必须将此委托设置为当前视图控制器并实现该方法。

mVC.delegate = self;

... 
some other place in code
...

- (void)myCollectionViewDidScrollToBottom:(myCollectionView *)collectionView {
    //do what's need to be done ;)
}

现在您的 "myCollectionView" 不在乎它的位置和 "who"。任何想要使用它的人都可以实现此方法并成为具有任何自定义逻辑的视图的委托。也就是说,在此方法中,您可以调用任何必需的方法和任何其他东西。

这是如何工作的:

  1. 向下滚动时查看通知它的委托关于此事件
  2. 委托处理所有需要完成的逻辑

:)

首先我想对大家说声谢谢。

我通读了你们写的所有内容,我受到了启发。

尤其是 Stanislav Ageev 的回答,他说

You're getting the superview instead of the parent controller...

.

我试图将控制器本身作为 UIViewController 而不是控制器视图传递。

而且我还从 scrollViewDidScroll 中删除了 @selector() 并直接传递了选择器。

编辑 我错过了 zy.lius 答案。感谢 him/her 发布了一个答案,该答案基本上解释了我在这里所做的事情,即使我自己想出来了。对不起。

我的代码现在看起来像这样 (.h/.m):

控制器

....

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self initCollectionView];
}
....

- (void) getMoreInfo{
    NSLog(@"Getting more info");
}

- (void) initCollectionView{
    UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc]init];
    flowLayout.itemSize = CGSizeMake(50.0, 60.0);
    flowLayout.sectionInset = UIEdgeInsetsMake(20, 0, 20, 0);
    [flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
    flowLayout.headerReferenceSize = CGSizeMake(320.f, 30.f);

    myCollectionView *mVC = [myCollectionView alloc] init: self: flowLayout: @selector(getMoreInfo)];
}

myCollectionView.h 文件:

#import <UIKit/UIKit.h>

@interface myCollectionView : UICollectionView<UIScrollViewDelegate, UICollectionViewDelegate, UICollectionViewDataSource>{
    UICollectionView *parentController;
    SEL onScrolledToBottom;
}

@property UICollectionView *parentController;
@property SEL onScrolledToBottom;

- (id)init: (UICollectionView*) parent: (UICollectionViewLayout *)layout: (SEL)onScrolledToBottomSEL;
@end

和 .m 文件:

#import "myCollectionView.h"

@implementation myCollectionView

@synthesize parentController = parentController;
@synthesize onScrolledToBottom = onScrollToBotton;

- (id)init: (UIView*) parent: (UICollectionViewLayout *)layout: (SEL)onScrolledToBottomSEL{
    self = [super initWithFrame: CGRectMake(0.0f, 0.0f, parent.view.frame.size.width, parent.view.frame.size.height) collectionViewLayout: layout];
    if (self) {
        parentController = parent;
        self.delegate = self;
        self.dataSource = self;
        onScrolledToBottom = onScrolledToBottomSEL;
    }

    return self;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    CGFloat offsetY = scrollView.contentOffset.y;
    CGFloat contentHeight = scrollView.contentSize.height;
    if (offsetY > contentHeight - scrollView.frame.size.height)
    {
        [parentController performSelector:@onScrolledToBottom withObject:nil];
    }
}

这工作得很好,可以用来以各种方式与主控制器通信。

而且非常简单。

再次感谢您启发我找到最有效的解决方案。