iOS 自定义警告框的实现

来源:互联网 发布:js跨域和导入js文件 编辑:程序博客网 时间:2024/06/05 09:09

iOS 自定义警告框的实现

热度 4已有 6631 次阅读 2012-8-17 23:44 |个人分类:UI界面|系统分类:移动互联网| 美化

这里的应用有两个方面:第一、控件的美化   第二、定义自己的控件

为了项目需求,我们有时会觉得官方的控件不好看,或者是因为控件的样式与软件整体风格不匹配,那么这个时候我们往往会对控件进行美化

有时我们需要的控件官方没有提供,比如radioGroup,那么这时我们不可能去请求官方为你去搞一个控件来,所谓自己动手丰衣足食,那么我们还是自己去定义好了

这里我们就两个控件进行说明,大家觉不觉得官方的警告框特别的单调,有时我们的软件程序是灰色调的,或者软件总体以红色,黄色为主的色调,它都是长那样,不妨自己定义好了,以前在论坛上发过自定义alert的例子,这次我们完善它一下

RAlertView
为了能达到官方使用一样的效果,我们定义的函数名和官方的类似,传递的参数一样,使用方法一样,这样别人在使用你的控件时不用教也知道怎么用,否则你的自 定义就变得没有意义了,代码最经典的地方就是重用,好的代码大家都会去使用,有很多的非官方的代码我们大家都在使用它,那是因为那些代码封装的好,大家觉 得好用,如果别人为了使用你写的代码发现要更改很大,或者干脆不会用,大家是不愿意浪费时间去研究你的代码的,方便是最终的宗旨

废话少说,开始吧

警告框的基本功能有:一、提示作用(可能你进行了非法操作,可能某些事件达到了促发点,怕用户不知道) 二、对用户操作的确认,有时我们可能需要进行一些不可逆转的操作,也有可能是误点了,那么这时进一步的确认是必要的

警告框的特性:我们在有警告的时候是不能进行其他操作的,只能操作警告框为我们提供的选项

我们想要绘制一个比较好看的警告框,我们需要它可以任意设置背景色,背景图片
@class RAlertView;

@protocol RAlertViewDelegate <NSObject>
@optional
//警告框的按钮点击事件,与官方警告框点击函数一样,唯一的就是传递的警告框变成了我们 自己定义的
- (void)alertView:(RAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;

@end

@interface RAlertView : UIView{
    UIImageView *backgroundView;                 //我们希望我们的警告框可以更改任意的背景图片
        id<RAlertViewDelegate> _delegate;
   
    UIWindow    *modelWindow;            //警告框架构的window
    UIWindow    *resignWindow;           //警告框之前的key window,用于警告框消失后恢复第一响应
}
@property(nonatomic,assign)id<RAlertViewDelegate> _delegate;

//初始化函数(和官方提供的一样)
- (id)initWithTitle:(NSString *)title message:(NSString *)message delegate:(id )delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... ;

//设置背景(增加了自己的可以更改背景色的功能)
-(void)setBackground:(UIImage*)image;

//显示警告框
-(void)show;

//警告框消失
- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated;

@end

看还缺什么,基本官方提供的功能我们都可以实现了,下面就是绘制了,注意绘制不能写死,我们写的控件是要给别人用的,所以,别人在用你的控件的时候,只要将文件放到工程下,加入头文件就可以像普通控件那样正常使用了。

在初始化函数中先将基本的大背景设置好
        self.backgroundColor=[UIColor grayColor];
        modelWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        [modelWindow setBackgroundColor:[UIColor blackColor]];
        modelWindow.alpha = 0.8;
        resignWindow   = [[UIApplication sharedApplication] keyWindow];
        _delegate = delegate;

然后一个一个来,首先是绘制标题
        //set title
        UIFont *titlefont = [UIFont boldSystemFontOfSize:18];
        CGSize titlesize = [title sizeWithFont:titlefont constrainedToSize:CGSizeMake(180.0f, 1000.0f) lineBreakMode:UILineBreakModeCharacterWrap];
        
        UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0f, 10.0f, titlesize.width+5, titlesize.height+5)];
        titleLabel.backgroundColor = [UIColor clearColor];
        titleLabel.textColor=[UIColor whiteColor];
        titleLabel.font = titlefont;
        titleLabel.numberOfLines = 0;
        [titleLabel setTextAlignment:UITextAlignmentCenter];
        titleLabel.lineBreakMode = UILineBreakModeCharacterWrap;
        titleLabel.text = title;
我们设置的大小都是根据数据来的,这样就不会将控件写死了,
同样的,再来设置消息提示
        //set message
        UIFont *messagefont = [UIFont systemFontOfSize:16];
        CGSize messagesize = [message sizeWithFont:messagefont constrainedToSize:CGSizeMake(200.0f, 1000.0f) lineBreakMode:UILineBreakModeCharacterWrap];
        UILabel *messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(10.0f, 10.0f+titlesize.height+8, messagesize.width+5, messagesize.height+5)];
        messageLabel.backgroundColor = [UIColor clearColor];
        messageLabel.textColor=[UIColor whiteColor];
        messageLabel.font = messagefont;
        messageLabel.numberOfLines = 0;
        [messageLabel setTextAlignment:UITextAlignmentCenter];
        messageLabel.lineBreakMode = UILineBreakModeCharacterWrap;
        messageLabel.text = message;
接下来绘制按钮,按钮有两种,一个是cancelButton,一个是otherButton,
需要注意的有两点,cancelButton可能有可能没有,otherButton可能有可能没有,还有可能是多个,
首先判断cancelButton的存在与否
        NSInteger index = 0;
        if (cancelButtonTitle) {
            UIButton *cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
            [self addSubview:cancelBtn];
            [cancelBtn setTitle:cancelButtonTitle forState:UIControlStateNormal];
            [cancelBtn setBackgroundImage:[UIImage imageNamed:@"cancel_btn"] forState:UIControlStateNormal];
            [cancelBtn addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
            cancelBtn.tag=100;
            index++;
        }
注意我声明了一个index,这是干嘛的呢,这是所有button的tag标记以及记录button个数的标记,我在第一个button时并没有将tag 设置为0,这是因为所有view的初始tag都是0,如果设置为0,以后在取view的时候会出现意想不到的错误,请务必记住这点

        va_list args;
        
        if (otherButtonTitles) {
            UIButton *otherbutton = nil;
            otherbutton = [UIButton buttonWithType:UIButtonTypeCustom];
            [self addSubview:otherbutton];
            [otherbutton setTitle:otherButtonTitles forState:UIControlStateNormal];
            [otherbutton setBackgroundImage:[UIImage imageNamed:@"other_btn"] forState:UIControlStateNormal];
            [otherbutton addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
            if (index == 0) {
                otherbutton.tag = 100;                        //这里判断cancelButton是否存在
            }else{
                otherbutton.tag = index;               
            }
            index++;
            
            NSString *buttonTitle = nil;

            va_start(args,otherButtonTitles);

            while ((buttonTitle=va_arg(args, NSString*))) {
               
                otherbutton = [UIButton buttonWithType:UIButtonTypeCustom];
                [self addSubview:otherbutton];
                [otherbutton setTitle:buttonTitle forState:UIControlStateNormal];
                [otherbutton setBackgroundImage:[UIImage imageNamed:@"other_btn"] forState:UIControlStateNormal];
                [otherbutton addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
               
                otherbutton.tag = index;                //为之后的每个button设置tag
                index++;
            }
            va_end(args);
        }
该注释的地方我已经注释了,va_list不会用的自己去看看吧,这个不难
有没有发现至始至终我还没有设置每一个视图的frame,因为button的个数是不定的,现在我们已经知道了所有的视图了,是时候一次性绘制他们了,他们都已经准备好了,
        [self addSubview:titleLabel];
        titleLabel.center=CGPointMake(120, titleLabel.center.y);
        [titleLabel release];
        
        [self addSubview:messageLabel];
        messageLabel.center=CGPointMake(120, messageLabel.center.y);
        [messageLabel release];
        
        [self setButtonFrame:index offY:titlesize.height+messagesize.height];
        [self setCenter:modelWindow.center];
        
        self.layer.cornerRadius=12.0;
        backgroundView=[[UIImageView alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
        backgroundView.layer.cornerRadius=12.0;
        backgroundView.clipsToBounds = YES;
        [self addSubview:backgroundView];
        [self sendSubviewToBack:backgroundView];
由于button是一组相似的控件,所以我单独的将button的设置写在了一个方法里
-(void)setButtonFrame:(NSInteger)index offY:(CGFloat)offsetY{
    UIButton *button = nil;

    if (index == 2) {                //两个button的情况下,button并排显示
        button  = (UIButton*)[self viewWithTag:100];
        [button setFrame:CGRectMake(20, offsetY+40, 90, 40)];
        
        button  = (UIButton*)[self viewWithTag:1];
        [button setFrame:CGRectMake(130, offsetY+40, 90, 40)];
        
        [self setFrame:CGRectMake(0, 0, 240, offsetY+100)];
        
    }else{                                        //其他情况的button,纵排显示
        for (int i =0 ; i<index; i++) {
            if (i== 0) {
               button  = (UIButton*)[self viewWithTag:100];
            }else{
                button  = (UIButton*)[self viewWithTag:i];
            }
            [button setFrame:CGRectMake(20, offsetY+(i+1)*45, 200, 40)];
        }
        [self setFrame:CGRectMake(0, 0, 240, offsetY+60+45*index)];
    }
}

所有的视图都确定好了,那么我们的警告框的大小也就确定了
值得一提的一点是,请将设置button的函数声明为私有的,我可不想在外面去调用它,外界调用它时,视图将会变的一塌糊涂,既然外界无权调用,那就声明为私有的
@interface RAlertView(private)

-(void)setButtonFrame:(NSInteger)index offY:(CGFloat)offsetY;
   
@end
我们的绘制还没完,我们要支持背景图片
//设置背景
-(void)setBackground:(UIImage*)image{
    CGSize size = [image size];
    CGFloat width=0,height=0;
    //图片宽度较小
    if (240-size.width>0) {
        width = 240-size.width;
    }
    //图片高度不够
    if (self.frame.size.height-size.height>0) {
        height = self.frame.size.height-size.height;
    }
    [image stretchableImageWithLeftCapWidth:width topCapHeight:height];
    [backgroundView setImage:image];
}
这里我稍微的对其做了一点优化,就是在设置的图片不够大的时候,自由拉伸图片而不使图片失真。

数据,绘制已经搞定了,那现在轮到操作了,首先我们第一步操作是让它显示出来
//显示警告框
-(void)show{
    [modelWindow makeKeyAndVisible];
        [modelWindow addSubview:self];
        // Bounce to 1% of the normal size
        self.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
       
    [UIView animateWithDuration:0.15 animations:^{
        // Return to 120%
        self.transform = CGAffineTransformMakeScale(1.1f, 1.1f);
    }completion:^(BOOL finished) {
        [UIView animateWithDuration:0.15 animations:^{
            // Return back to 100%
            self.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
        }];
    }];

}
这块主要是动画的使用,比较简单,这里就不多做诠释了

至此,我们的警告框已经实现了警告的功能了,我可不想警告了我之后它一直出现在这里,那样我没法操作了啊
于是我们写了一个dismiss函数,这和官方的函数是一样的,使用也是一样
//警告框消失
- (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated{
    if (self) {
        if (animated) {
            [UIView animateWithDuration:0.2f
                             animations:^{
                                 self.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
                             }completion:^(BOOL finished){
                                 [modelWindow resignKeyWindow];
                                 [resignWindow makeKeyAndVisible];
                                 [self release];
            }];
        }else{
            [modelWindow resignKeyWindow];
            [resignWindow makeKeyAndVisible];
            [self release];
        }
    }
}
在使用动画的时候,不能立即更改窗口的第一响应,否则,我们将看不到任何动画效果
我们的警告框还不能响应用户的选择操作
-(void)buttonClicked:(id)sender{
    if (_delegate&&[_delegate respondsToSelector:@selector(alertView:clickedButtonAtIndex:)])
    {
        NSInteger index = [sender tag];
        if (index == 100) {
            index = 0;
        }
        [_delegate alertView:self clickedButtonAtIndex:index];
    }
    [modelWindow resignKeyWindow];
    [resignWindow makeKeyAndVisible];
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(dismissWithClickedButtonIndex:animated:) object:nil];                //有可能我们事先调用了dismiss函数,再次我们取消dismiss函数
    [self release];                                        //视图消失,释放掉
}
无论是dismiss函数还是我们的button响应函数,既然视图不存在了,别忘了释放我们的控件

我们已经完整的自定义了自己的警告框了 ,看看我们写的能不能被别人使用,现在我们新建一个pad版本的工程,将我们写的文件以及资源文件拖到工程下,我们没有做任何处理,警告框显示正常,这正是我们想要的,这才是真正的自定义控件
0 0
原创粉丝点击