【问题标题】:Objective-C Custom Class Actions and OutletsObjective-C 自定义类操作和出口
【发布时间】:2013-08-30 09:19:33
【问题描述】:

我正在尝试创建 UIView 的自定义子类,如下所示:

我创建了一个带有 UIView 的 .xib,其中包含一个 Picker 对象和 Toolbar 对象,连接了 Outlets 和操作。

CustomPickerView.h

#import <UIKit/UIKit.h>

@interface CustomPickerView : UIView

@property (strong, nonatomic) IBOutlet UIDatePicker* datePicker;
@property (strong, nonatomic) IBOutlet UIBarButtonItem* doneButton;

-(IBAction) buttonDonePush:(id)sender;

@end

CustomPickerView.m

#import "CustomPickerView.h"

@implementation CustomPickerView

-(id) init
{
    self=[[[NSBundle mainBundle] loadNibNamed:@"CustomPickerView" owner:self options:nil] objectAtIndex:0];
    return self;
}

-(void) buttonDonePush:(id)sender
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"CustomPickerViewDoneButtonPush" object:nil userInfo:[NSDictionary dictionaryWithObject:self.datePicker.date forKey:@"date"]];
}

@end

最后,在我的 ViewController 中,我在 viewDidLoad 方法中实例化对象。

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.customPickerView=[[CustomPickerView alloc] init];
    self.customPickerView.datePicker.datePickerMode=UIDatePickerModeTime;

    self.dateField.inputView=self.customPickerView;
}

当用户点击 self.dateField 时,我的 CustomPickerView 会很好地弹出来代替标准键盘。

问题是当用户从我的 CustomPickerView 类中点击完成按钮时,buttonDonePush 操作不会触发。

【问题讨论】:

  • 你怎么知道它不会触发?您是否已将控制器添加为该通知的观察者?
  • 你把doneButton的目标设置为outlet了吗?
  • self=[[[NSBundle mainBundle] loadNibNamed:@"CustomPickerView" owner:self options:nil] objectAtIndex:0]; xib 的所有者为 nil,因为 self 未初始化。
  • 你为什么不使用委托,而不是过度杀伤/不适当的通知?
  • 查看我的答案下方的 cmets。

标签: ios objective-c class uiview iboutlet


【解决方案1】:

这个答案可以看作是我最近为 iOSX 提供的类似解决方案的 iOS 伴侣:

Interface-Builder: "combine" NSView-class with .xib

你的安排是这样的:

  • Mainstoryboard.storyboard
  • MyViewController.h
  • MyViewController.m

  • CustomPickerView.xib

  • CustomPickerView.h
  • CustomPickerView.m

您希望将您的 customPickerView 用作 MyViewController.view 的子视图,并希望能够从包含的上下文中访问它的控件小部件。

在您的示例中,您正在代码中创建 customPickerView,但另一个有用的场景是将其添加到 Interface Builder 的情节提要中。此解决方案适用于这两种情况。

在 CustomViewPicker.h 中

  • 为您的界面元素声明 IBOutlets。您已经为 datePicker 和 doneButton 完成了此操作,但您还需要一个 IBOutlet 到 UIView,这将是这些项目的包含视图。

    @property (strong, nonatomic) IBOutlet UIView* view;
    

在 CustomViewPicker.xib 中

  • 在 Identity Inspector 中将文件的所有者类设置为 CustomViewPicker。
  • 将 xib 中的顶级视图设置为默认 UIView 类(NOT CustomViewPicker)。
  • 将来自文件所有者的 IBOutlets:viewdatePickerdoneButton 连接到它们各自的 IB 对象
  • 将您的 IBAction 从文件所有者:buttonDonePush 连接到 doneButton IB 对象

在 CustomViewPicker.m 中:

- (id)initWithFrame:(CGRect)frame
{
    //called when initialising in code
    self = [super initWithFrame:frame];
    if (self) {
        [self initialise];
    }
    return self;
}

  - (void)awakeFromNib
  {
      //called when loading from IB/Storyboard
      [self initialise];
  }

- (void) initialise
{
    NSString* nibName = NSStringFromClass([self class]);
    if ([[NSBundle mainBundle] loadNibNamed:nibName
                                      owner:self
                                    options:nil]) {
        [self.view setFrame:[self bounds]];
        [self addSubview:self.view];
    }

}
-(void) buttonDonePush:(id)sender
{
   //button push actions
}

如果您想在代码中初始化(如您所做的那样),您的 MyViewController 将包含如下内容:

- (void)viewDidLoad
{
    [super viewDidLoad];
    CGRect frame = CGRectMake(0, 50, 320, 300);
    self.customPickerView=[[CustomPickerView alloc] initWithFrame:frame];

    self.customPickerView.datePicker.datePickerMode=UIDatePickerModeTime;
    self.dateField.inputView=self.customPickerView;
}

[edit 删除了这个多余的行:[self.view addSubview:self.customPickerView];]

或者,您可以直接在情节提要中创建您的 CustomPickerView - 并设置它的框架。只需将自定义视图添加到 MyViewController 的故事板场景,并将其类更改为 CustomPickerView。将其链接到您的 self.customPickerView IBOutlet。

在这种情况下,initWithFrame 不会被调用,但是当 MyViewController 加载它的 CustomPickerView 子视图时会调用awakeFromNib。您的 MyViewController 的 viewDidLoad 将如下所示:

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.customPickerView.datePicker.datePickerMode=UIDatePickerModeTime;
    self.dateField.inputView=self.customPickerView;
}

如果您想从 customPickerView 中获取按钮推送操作,您可以考虑使用委托,它可能比您使用 NSNotification 更加独立(但该问题超出了您最初的问题)。

【讨论】:

  • 您的代码工作正常,除了 viewDidLoad 方法中的 [self.view addsubView:self.customPickerView] 不是必需的。一旦按照您的解释实现了它,我就能够快速解决我遇到的其他问题。顺便说一句,我使用来自 CustomPickerView 的通知的原因是因为我无法访问 ViewController 和 CustomPickerView 之间的对象属性。多亏了您的帮助,我才得以让一切正常运转。谢谢!!
  • @DanCooley 很高兴能提供帮助。感谢您提出这个问题,我正在寻找机会记录这一点。这不是一个新手问题,Apple(或其他地方)的文档记录非常差。我已经更正了那条多余的线,尽管没有它我不明白视图是如何附加到 viewController 的视图上的(确实如此,而且视图层次结构似乎是正确的,我只是不明白 如何它确实)。
【解决方案2】:

编辑:

上面的答案指出了这一点,但是在 init 方法中您正在设置 self,但这发生在 self 初始化之前。如果您可以在创建此特定视图的位置显示代码,那将有很大帮助。这是我的建议。

在您控制此自定义视图部署的类中:

//to invoke your view
CustomPickerView *myView;
NSArray *xibContents = [[NSBundle mainBundle]loadNibNamed:@"CustomPickerView" owner:nil options:nil];
for (id xibObject in xibContents) {
    if ([xibObject isKindOfClass:[CustomPickerView class]]) {
        myView = (CustomPickerView *)xibObject;
        break;
    }
}
//now *myView is instantiated as your custom picker view
//do what you want here, add to subview, set frame, etc

在 CustomPickerView.m 文件中,删除 init 方法。

以前的答案:

您在此实现中使用 NSNotificationCenter。当用户触摸完成按钮时,会发布一个NSNotification。您必须明确“选择加入”和“收听”这些通知。您可以通过在通知中心注册来做到这一点。

在 viewDidLoad 中:

 [[NSNotificationCenter defaultCenter] addObserver:self
    selector:@selector(receivedNotification:) 
    name:@"CustomPickerViewDoneButtonPush"
    object:nil];

然后你需要实现你在那里指定的选择器:

-(void)receivedNotification:(NSNotification *)note {
    NSDictionary *obj = [note object];
    NSLog(@"%@",obj);
    //dismiss the date picker here...
    //etc...
}

【讨论】:

  • 这可能是它。但我最好告诉你,在这里使用通知是一件很奇怪的事情,很可能;委托更合适。
  • 我同意在这里使用通知很奇怪,因为委托也会很奇怪:这都在同一个类中,没有理由他不能调用这个方法自我而不是发布通知
  • 谢谢,但这不是问题所在。问题是为什么当用户点击 Done 按钮时没有调用 -(IBAction) buttonDonePush 方法?一旦调用,通知将触发,我将在其他地方处理该事件。
  • 我明白了。您在哪里将 UIBarButton 项连接到此 IBAction?仅将 IBOutlet 与按钮链接是不够的,您还必须告诉按钮它应该在用户点击时调用相关选择器。
  • @JustinAmberson 我在 Interface Builder 中连接它。从工具栏中将接收到的操作“buttonDonePush”设置为条形按钮项。看看上面的图片。这整个事情有点棘手,因为我使用 Storyboard 作为我的 ViewController 和一个 .xib 来为 CustomPicker 建模。我一定有某种实例化误解。
猜你喜欢
  • 1970-01-01
  • 2012-09-16
  • 1970-01-01
  • 1970-01-01
  • 2011-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-16
相关资源
最近更新 更多