UIView不保留子视图

时间:2011-01-10 13:46:42

标签: iphone objective-c uiview retain

我进行了播放/暂停/停止控制,但它没有按预期工作。在加载时,它创建三个视图并将它们存储在一个数组中,每个视图代表一个控件的状态。在停止状态下,它包含一个播放按钮,它是我制作的简单类集群的成员。在其他两个状态中,它包含一个UIView,其中两个按钮作为子视图。在第一个状态下它可以工作并完全按照预期的方式运行,但是当它尝试进入下一个状态时,它会查看数组并在播放状态下查找视图并暂停状态位置而不显示子视图。实际上,如果你通过执行loadView函数来跟踪它,那么即使我调用了addSubview:(UIView *)视图,该数组也永远不会获得带有子视图的视图,该文档说明了这一点:此方法保留查看并设置接收器的下一个响应器,这是它的新超视图。

我真的希望得到一些帮助,试图理解为什么会这样。为了更清楚,为什么传递给数组的UIViews在它们的局部变量没有子视图的情况下不会。

提前致谢,

这是来源:

// IMSpeechControl.h

#import <UIKit/UIKit.h>
#import "IMSpeechEngine.h"

typedef enum {
    IMSpeechControlStateStopped = 0,
    IMSpeechControlStatePlaying = 1,
    IMSpeechControlStatePaused = 2
} IMSpeechControlState;

/* State Stopped: speech control should show a Play button.
   State Playing: speech control should show a Pause button and a Stop button.
   State Paused : speech control should show a Play button and a Stop button.
*/

@class IMSpeechControl;

@protocol IMSpeechControlDelegate <NSObject>

- (NSString *)speechControlNeedsText:(IMSpeechControl *)sender;


@end


@interface IMSpeechControl : UIViewController {
    IMSpeechControlState controlState;
    id delegate;
    IMSpeechEngine *speechEngine;
    NSMutableArray *controlButtons_;
    CGRect frame_;
}

@property (nonatomic, readonly) IMSpeechControlState controlState;
@property (nonatomic, assign) id<IMSpeechControlDelegate> delegate;

// Designated initilazer
- (IMSpeechControl *)initWithFrame:(CGRect)frame;

// This must be here, do not call it from outside it's control buttons
- (void)changeToState:(IMSpeechControlState)controlState;

- (void)play;
- (void)pause;
- (void)stop;


@end

这是重要的一个。

// IMSpeechControl.m

#import "IMSpeechControl.h"
#import "IMSpeechControlButton.h"

@interface IMSpeechControl ()

@property (nonatomic, assign) IMSpeechEngine *speechEngine;
@property (nonatomic, retain) NSMutableArray *controlButtons;
// Used  only for initialization, do not change after calling initWithFrame
// to change the view size after creation 
@property (nonatomic) CGRect frame;

- (void)speechEngineDidFinishSpeaking:(NSNotification *)notifictation;

@end


@implementation IMSpeechControl

@synthesize controlState, delegate, speechEngine, frame=frame_;
@synthesize controlButtons=controlButtons_;

/* State Stopped: speech control should show a Play button.
 State Playing: speech control should show a Pause button and a Stop button.
 State Paused   : speech control should show a Play button and a Stop button.
 */

- (IMSpeechControl *)initWithFrame:(CGRect)aFrame {
    if (self = [super init]) {
        self.frame = aFrame;
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(speechEngineDidFinishSpeaking:) name:kDidFinishSpeakingNotificationName object:self.speechEngine];
    }
    return self;
}

- (void)loadView {

    // Initialization code.

    // Create the main view.
    UIView *aView = [[UIView alloc] initWithFrame:self.frame];
    self.view = aView;
    [aView release];

    // Create the sub-views and store them in an array.
    NSMutableArray *controls = [[NSMutableArray alloc] initWithCapacity:3];

    // The stopped state play button view can be used directly since it is the only button shown.
    IMSpeechControlButton *button = [[IMSpeechControlButton alloc] initWithFrame:self.frame forControl:self style:IMSpeechControlButtonStylePlay];
    [controls insertObject:button atIndex:(NSUInteger)IMSpeechControlStateStopped];
    [button release];

    // The other two states require two buttons each, so the two buttons must be grouped into a UIView that can be easily switched out.
    // Make two frames, one for the left and one for the right. Both are half the width of the main view
    // The one on the left has the same origin as the main view...
    CGRect halfFrameLeft = CGRectMake(frame_.origin.x, frame_.origin.y, frame_.size.width / 2, frame_.size.height);
    // and the one on the right starts half-way across the main view
    CGRect halfFrameRight = CGRectMake((frame_.origin.x + (frame_.size.width / 2)), frame_.origin.y, frame_.size.width / 2, frame_.size.height);

    // Playing state
    // Pause button
    UIView *playingState = [[UIView alloc] initWithFrame:self.frame];
    IMSpeechControlButton *plsPauseButton = [[IMSpeechControlButton alloc] initWithFrame:halfFrameLeft forControl:self style:IMSpeechControlButtonStylePause];
    [playingState addSubview:plsPauseButton];
    [plsPauseButton release];
    // Stop button
    IMSpeechControlButton *plsStopButton = [[IMSpeechControlButton alloc] initWithFrame:halfFrameRight forControl:self style:IMSpeechControlButtonStyleStop];
    [playingState addSubview:plsStopButton];
    [plsStopButton release];
    [controls insertObject:playingState atIndex:(NSUInteger)IMSpeechControlStatePlaying];
    [playingState release];

    // Paused state
    // Play button
    UIView *pausedState = [[UIView alloc] initWithFrame:self.frame];
    IMSpeechControlButton *pasPlayButton = [[IMSpeechControlButton alloc] initWithFrame:halfFrameLeft forControl:self style:IMSpeechControlButtonStylePlay];
    [pausedState addSubview:pasPlayButton];
    [pasPlayButton release];
    // Stop button
    IMSpeechControlButton *pasStopButton = [[IMSpeechControlButton alloc] initWithFrame:halfFrameRight forControl:self style:IMSpeechControlButtonStyleStop];
    [pausedState addSubview:pasStopButton];
    [pasStopButton release];
    [controls insertObject:pausedState atIndex:(NSUInteger)IMSpeechControlStatePaused];
    [pausedState release];
    // store the array in an instance variable
    self.controlButtons = controls;
    [controls release];

    // Set the view to it's first state (stopped)
    IMSpeechControlButton *stoppedState = (IMSpeechControlButton *)[self.controlButtons objectAtIndex:(NSUInteger)IMSpeechControlStateStopped];
    [self.view addSubview:stoppedState];
    controlState = IMSpeechControlStateStopped;
}


- (IMSpeechEngine *)speechEngine {
    if (nil == speechEngine) {
        self.speechEngine = [IMSpeechEngine sharedManager];
    }
    return speechEngine;
}

- (void)changeToState:(IMSpeechControlState)state {
    // This line caused my problem
    // IMSpeechControlButton *currentView = [[self.view subviews] objectAtIndex:0];
    // It should look like this
    UIView *currentView = [[self.view subviews] objectAtIndex:0];
    switch (state) {
        case IMSpeechControlStateStopped:
        {
            UIView *stoppedState = (UIView *)[self.controlButtons objectAtIndex:(NSUInteger)IMSpeechControlStateStopped];
            [self.view addSubview:stoppedState];
            [UIView animateWithDuration:0.15 
                             animations:^{
                                 currentView.alpha = 0.5;
                                 stoppedState.alpha = 0.15;
                             }
                             completion:^(BOOL finished)
                                 currentView.alpha = 0.0;
                                 [currentView removeFromSuperview];
                                 [UIView animateWithDuration:0.15 animations:^{stoppedState.alpha = 0.5;}];
                             }];
            controlState = IMSpeechControlStateStopped;
            break;
        }
        case IMSpeechControlStatePlaying:
        {
            UIView *playingState = [self.controlButtons objectAtIndex:(NSUInteger)IMSpeechControlStatePlaying];
            [self.view addSubview:playingState];
            [UIView animateWithDuration:0.15 
                             animations:^{
                                 currentView.alpha = 0.5;
                                 playingState.alpha = 0.15;
                             }
                             completion:^(BOOL finished){
                                 currentView.alpha = 0.0;
                                 [currentView removeFromSuperview];
                                 [UIView animateWithDuration:0.15 animations:^{playingState.alpha = 0.5;}];
                             }];
            controlState = IMSpeechControlStatePlaying;
            break;
        }
        case IMSpeechControlStatePaused:
        {
            UIView *pausedState = (UIView *)[self.controlButtons objectAtIndex:(NSUInteger)IMSpeechControlStatePaused];
            [self.view addSubview:pausedState];
            [UIView animateWithDuration:0.15 
                             animations:^{
                                 currentView.alpha = 0.5;
                                 pausedState.alpha = 0.15;
                             }
                             completion:^(BOOL finished){
                                 currentView.alpha = 0.0;
                                 [currentView removeFromSuperview];
                                 [UIView animateWithDuration:0.15 animations:^{pausedState.alpha = 0.5;}];
                             }];
            controlState = IMSpeechControlStatePaused;
            break;
        }
        default:
            NSLog(@"Error %lu is not a recognized IMSpeechControlState", state);
            break;
    }
}

- (void)speechEngineDidFinishSpeaking:(NSNotification *)notifictation {
    // This notification is only sent if it has finished speaking and is therefore stopped.
    [self changeToState:IMSpeechControlStateStopped];
}

- (void)play {
    NSString *text = [delegate speechControlNeedsText:self];
    [self.speechEngine speakText:text];
    [self changeToState:IMSpeechControlStatePlaying];
}
- (void)pause {
    [self.speechEngine pauseSpeaking];
    [self changeToState:IMSpeechControlStatePaused];
}
- (void)stop {
    [self.speechEngine stopSpeaking];
    [self changeToState:IMSpeechControlStateStopped];
}


- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self forKeyPath:kDidFinishSpeakingNotificationName];
    [super dealloc];
}


@end

// IMSpeechControlButton.h

#import "IMSpeechControl.h"

typedef enum {
    IMSpeechControlButtonStylePlay,
    IMSpeechControlButtonStylePause,
    IMSpeechControlButtonStyleStop
}IMSpeechControlButtonStyle;

@interface IMSpeechControlButton: UIView {
    IMSpeechControl *speechControl;
    UIImageView *imageView;
}

@property (nonatomic, assign) IMSpeechControl *speechControl;
@property (nonatomic, retain) UIImageView *imageView;

- (IMSpeechControlButton *)initWithFrame:(CGRect)aRect 
                              forControl:(IMSpeechControl *)control
                                   style:(IMSpeechControlButtonStyle)style;
@end

// IMSpeechControlButton.m

#import "IMSpeechControlButton.h"
#import <Foundation/NSObjCRuntime.h>

@implementation IMSpeechControlButton

@synthesize speechControl;
@synthesize imageView;


- (IMSpeechControlButton *)initWithFrame:(CGRect)aRect 
                              forControl:(IMSpeechControl *)control
                                   style:(IMSpeechControlButtonStyle)style 
{
    NSString *str;
    switch (style) {
        case IMSpeechControlButtonStylePlay:
            str = @"IMSpeechControlPlay";
            break;
        case IMSpeechControlButtonStylePause:
            str = @"IMSpeechControlPause";
            break;
        case IMSpeechControlButtonStyleStop:
            str = @"IMSpeechControlStop";
            break;
        default:
            break;
    }
    isa = NSClassFromString(str);
    // the speechControl must be set before calling subclass implementation of initWithFrame
    self.speechControl = control;
    return [self initWithFrame:aRect];
}


/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code.
}
*/

- (void)dealloc {
    [super dealloc];
}


@end

除了handleGesture方法在speechControl上调用相应的播放/暂停/停止功能外,所有控制按钮都具有与播放按钮完全相同的代码。我创建所有这些的唯一原因是每个人都可以拥有自己的图像,所以他们可以在改变状态之前播放不同的动画,但我还没有达到目的。 // IMSpeechControlPlay.h

#import "IMSpeechControlButton.h"


@interface IMSpeechControlPlay : IMSpeechControlButton {

}

- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer;

@end

// IMSpeechControlPlay.m

#import "IMSpeechControlPlay.h"


@implementation IMSpeechControlPlay


- (id)initWithFrame:(CGRect)frame {

    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code.
        // TODO: set the image view
        UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
        gestureRecognizer.numberOfTapsRequired = 1;
        gestureRecognizer.numberOfTouchesRequired = 1;
        gestureRecognizer.delaysTouchesBegan = YES;
        [self addGestureRecognizer:gestureRecognizer];
        [gestureRecognizer release];

    }
    return self;
}

- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer {
    if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
        [speechControl play];
    }
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code.
}
*/

- (void)dealloc {
    [super dealloc];
}


@end

2 个答案:

答案 0 :(得分:1)

我发现了问题,就在这一行:

在changeState方法中

IMSpeechControlButton *currentView = (IMSpeechControlButton *)[[self.view subviews] objectAtIndex:0];

我在开发过程中多次更改并移动了这一行,并且该文件的旧版本正确地声明为:

UIView *currentView = [[self.view subviews] objectAtIndex:0]; 但我只是注意到我正在构建的文件有第一个版本。我复制源代码的版本是一个看起来像这样的旧版本:

UIView *currentView = (IMSpeechControl *)[[self.view subviews] objectAtIndex:0]; 将其更改为UIView指针使其工作。看看调试信息,看起来我错误的是没有保留子视图,实际上只有在按下播放控件时抛出的调试器中它们才可用。在调用init之前设置speechControl变量实际上工作正常。

感谢所有的建议,并且很快。

答案 1 :(得分:0)

这种方法真的错了

- (IMSpeechControlButton *)initWithFrame:(CGRect)aRect 
                          forControl:(IMSpeechControl *)control
                               style:(IMSpeechControlButtonStyle)style 

基本上没有什么可以回到这里

self.speechControl = control;
return [self initWithFrame:aRect];

因为你还没有真正进入“自我”。您需要重写该函数以获得与IMSpeechControlPlay中的初始化程序类似的正确初始化程序。