如何在一个背景图上设置点击块

2019-5-31 王建伟 iOS开发

项目过程中遇到一个这样的问题,有一个大的背景图(如下图所示),图上设置好了1,2,3这样的按键快,我们需要给这些无规则的快设置点击事件!

目录春.jpg

解决思路,首先把图片放到界面中,犹豫不同机型尺寸不同,这个地方需要注意调整界面尺寸:

UIScrollView*scrollView=[[UIScrollView alloc]initWithFrame:SPFrame(0, 0, SPScreen_W, SPScreen_H)];
    scrollView.contentInsetAdjustmentBehavior=UIScrollViewContentInsetAdjustmentNever;
    scrollView.bounces=NO;
    scrollView.showsVerticalScrollIndicator=NO;
    CGFloat h=SPScreen_W*1056/375.0f;
    scrollView.contentSize=CGSizeMake(SPScreen_W,h );
    
    UIImageView*bgImageView=[[UIImageView alloc]initWithFrame:SPFrame(0, 0, SPScreen_W, h)];
    bgImageView.image=[UIImage imageNamed:@"course_a"];
    [scrollView addSubview:bgImageView];
    [self.view addSubview:scrollView];

然后我们给背景图添加一个点击的手势

 bgImageView.userInteractionEnabled=YES;
    UITapGestureRecognizer*tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapGestureEvent:)];
    [bgImageView addGestureRecognizer:tapGesture];

现在我们需要确定这些点击块的中心,这边当然不要一个一个去测量,作为一个程序员当然要用程序来解决,这边可以在点击手势的事件中直接打印点击的点的坐标,记录下来即可,然后将这些点做一下处理保存到数组points中,处理方法如下

-(NSValue*)changeOriginalPoint:(CGPoint)opoint{
    CGFloat w=SPScreen_W;
    CGFloat h=w*1056.0f/375.0f;
    CGFloat x=opoint.x*w/375.0f;
    CGFloat y=opoint.y*h/1056.0f;
    CGPoint point=CGPointMake(x, y);
    return [NSValue valueWithCGPoint:point];
}

现在我们需要处理的就是,判断我们点击的点是否在点击块中间

首先我们要利用上面点击块的中心点来设计一个点击区域

-(CGRect)center:(CGPoint)center  expandArea:(CGFloat)number{
    CGRect rect=SPFrame(center.x-number, center.y-number, number*2, number*2);
    return rect;
}

然后实现点击手势事件,判断当前点击的点是否在点击块中即可

-(void)tapGestureEvent:(UITapGestureRecognizer*)sender{
    CGPoint point = [sender locationInView:sender.view];
    if([self directPoint:point]>=0&&[self directPoint:point]<32){
    ActivityViewController*AVC=[[ActivityViewController alloc]init];
    NSInteger n=[self directPoint:point];
    AVC.message=[CourseMessage yy_modelWithDictionary:self.courseList[n]];
    [self.navigationController pushViewController:AVC animated:YES];
} 
} 
-(NSInteger)directPoint:(CGPoint)point{ 
   NSInteger n=9999;
   循环块:0-self.points 
{ 
   CGPoint opoint=[self.points[i] CGPointValue];
   CGRect frame=[self center:opoint expandArea:16];
   if(CGRectContainsPoint(frame, point)){
   n=I;
   break;
} 
} 
return n;
} 

评论(0) 浏览(1765)

录音波形图的实现

2019-5-24 王建伟 iOS开发

在项目中我们需要实现一个动画特效,就是根据录音的分贝数来显示动画,线条长度代表分贝数,线条左右位置,逐渐变淡来代表时间,话不多说,下面介绍代码

1.创建一个视图view的子类:RecordWaveView

一个数组属性:waves,用来存储分贝数据

一个方法:-(void)setRecordWaveWithDecibel:(CGFloat)decibel,用来传入分贝数据,显示动画

初始化视图

-(instancetype)initWithFrame:(CGRect)frame{
    if(self=[super initWithFrame:frame]){
        self.backgroundColor=[UIColor clearColor];
     
    }
    
    return self;
}

画动画图,设置一个数组waves,waves中存储的是分贝数,然后将数组画在试图上:

-(void)drawRect:(CGRect)rect{
    CGFloat w=self.frame.size.width/2;
    CGFloat h=self.frame.size.height;
{
      CGFloat n=[self.waves[i] floatValue];
        UIBezierPath*linePath=[UIBezierPath bezierPath];
        [linePath moveToPoint:SPPoint(w-30-i*5, (h-n)/2)];
        [linePath addLineToPoint:SPPoint(w-30-i*5, (h+n)/2)];
        linePath.lineWidth=2;
        [[UIColor colorWithRed:250/255.0 green:127/255.0 blue:0/255.0 alpha:1-i/10.0f] setStroke];
        [linePath stroke];
}循环块:0-self.waves.count的循环;
{
 CGFloat n=[self.waves[i] floatValue];
        UIBezierPath*linePath=[UIBezierPath bezierPath];
        [linePath moveToPoint:SPPoint(w+30+i*5, (h-n)/2)];
        [linePath addLineToPoint:SPPoint(w+30+i*5, (h+n)/2)];
        linePath.lineWidth=2;
        [[UIColor colorWithRed:250/255.0 green:127/255.0 blue:0/255.0 alpha:1-i/10.0f] setStroke];
        [linePath stroke];
}循环块:0-self.waves.count的循环;
}

实现方法,让画的视图动起来

-(void)setRecordWaveWithDecibel:(CGFloat)decibel{
    if(self.waves.count<10){ 
    [self.waves insertObject:[NSNumber numberWithFloat:decibel] atIndex:0];
 }
 else{ 
   [self.waves removeLastObject];
   [self.waves insertObject:[NSNumber numberWithFloat:decibel] atIndex:0];
 } 
   [self setNeedsDisplay]; 
}
 

评论(0) 浏览(3063)

项目中的工具类

2019-5-17 王建伟 iOS开发

项目中我们一般需要新建一个工具类,用来实现一些项目中经常使用的功能,方便复用,这边我收集了一些自己经常使用的功能

1.添加文字提示框

+(void)showHubTipWithString:(NSString *)string offsetY:(CGFloat)y{  
    if (string && string.length > 0) {
        dispatch_async(dispatch_get_main_queue(), ^{
            MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:[[UIApplication sharedApplication] keyWindow] animated:YES];
            hud.mode = MBProgressHUDModeText;
            hud.detailsLabel.font = [UIFont boldSystemFontOfSize:17.0];
            hud.detailsLabel.text =string;
            hud.margin = 20.f;
            hud.offset=SPPoint(0, y);
            hud.removeFromSuperViewOnHide = YES;
            [hud hideAnimated:YES afterDelay:0.8];
        });
    }    
}

2.添加加载提示框

+(MBProgressHUD*)showLoadingHubAddedTo:(UIView*)view{
    MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:view];
    hud.contentColor=[UIColor whiteColor];
    hud.bezelView.backgroundColor=[UIColor colorWithWhite:0.1 alpha:1];
    hud.mode = MBProgressHUDModeIndeterminate;
    hud.detailsLabel.font=SPFont(17);
    hud.minShowTime=2;
    hud.userInteractionEnabled = YES;
    hud.removeFromSuperViewOnHide = YES;
    [view addSubview:hud];
    return hud;
}

3.手机号验证

+(BOOL)validateMobile:(NSString*)mobile{
    NSString*mobileRegex=@"^(1[123456789][0-9]{9}$)";
    NSPredicate*mobileTest=[NSPredicate predicateWithFormat:@"SELF MATCHES %@",mobileRegex];
    return [mobileTest evaluateWithObject:mobile];
    
}

4.密码验证

+(BOOL)validatePassword:(NSString*)password{
    NSString*passwordRegex=@"^([a-zA-Z0-9]{6,12}$)";
    NSPredicate*passwordTest=[NSPredicate predicateWithFormat:@"SELF MATCHES %@",passwordRegex];
    return [passwordTest evaluateWithObject:password];  
}

5.获取段落行高

+(CGFloat)GetCellHeightWithString:(NSString *)string width:(CGFloat)width attributes:(NSDictionary *)attributes{
    
    
    
    CGRect sizestring = [string boundingRectWithSize:CGSizeMake(width,MAXFLOAT) options:
                        NSStringDrawingUsesLineFragmentOrigin| NSStringDrawingUsesFontLeading  attributes:attributes context:nil];
    
    return ceil(sizestring.size.height);
}

6.秒数到时间到转化

+(NSString*)changeNumberToTime:(NSInteger)number{
    NSString*minute=[NSString stringWithFormat:@"%02ld",number%3600/60];
    NSString*second=[NSString stringWithFormat:@"%02ld",number%60];
    return [NSString stringWithFormat:@"%@:%@",minute,second];
    
}

评论(0) 浏览(2473)

简单的网络加载图的实现

2019-5-10 王建伟 iOS开发

在编写项目的时候我们需要进行网络的加载,那么网络加载需要时间和重新加载的选项,所以我们可以需要一个加载界面,在网络加载的过程中,重新加载的时候呈现

编辑一个简单的网络加载界面,我们需要一个加载动画,以及加载成功或者失败的处理方法

1.建立一个UIView的子类,LoadingView

2.我们需要的属性:
a:CAShapeLayer*_loadingShapeLayer(制作简单动画);
b:UILabel*promptLabel(提示文字);
c:UIButton*reloadButton(重新加载按键) ;
d:LoadingFailBlock failBlock(加载失败的事件处理块)

3.初始化界面

+(LoadingView*)ShowLoadingViewFrame:(CGRect)frame withSuperView:(UIView *)view{
     LoadingView*loadingview=[[LoadingView alloc]initWithFrame:frame];
     [view addSubview:loadingview];
     [loadingview loadingShapeLayerAnimation];
     return loadingview;
}

-(instancetype)initWithFrame:(CGRect)frame{
    if(self=[super initWithFrame:frame]){
        self.backgroundColor=[UIColor whiteColor];
        _loadingShapeLayer=[[CAShapeLayer alloc]init];
        _loadingShapeLayer.frame=SPFrame(frame.size.width/2-40, frame.size.height/2-100, 80,80 );
        _loadingShapeLayer.path=[UIBezierPath bezierPathWithOvalInRect:SPFrame(0, 0, 80,80 )].CGPath;
        _loadingShapeLayer.fillColor=nil;
        _loadingShapeLayer.strokeColor=[UIColor lightGrayColor].CGColor;
        _loadingShapeLayer.lineWidth=2;
        _loadingShapeLayer.lineJoin=kCALineCapRound;
        _loadingShapeLayer.lineDashPattern=@[@15,@8];
        [self.layer addSublayer:_loadingShapeLayer];
        
        promptLabel=[[UILabel alloc]initWithFrame:SPFrame(frame.size.width/2-40, frame.size.height/2-100, 80,80 )];
        promptLabel.backgroundColor=[UIColor clearColor];
        promptLabel.text=@"正在加载";
        promptLabel.textColor=[UIColor lightGrayColor];
        promptLabel.font=SPFont(15);
        promptLabel.textAlignment=NSTextAlignmentCenter;
        [self addSubview:promptLabel];
      
    }
    
    return self;
}

4.实现方法:加载成功

-(void)LoadingSuccessComplation:(void (^)(void))complation{
    SPSelf;
    [UIView animateWithDuration:2 delay:0 usingSpringWithDamping:1 initialSpringVelocity:0.0f options:UIViewAnimationOptionLayoutSubviews animations:^{
        weakSelf.transform=CGAffineTransformMakeScale(0.001, 0.001);
    } completion:^(BOOL finished) {
        [weakSelf removeFromSuperview];
        
    }];
    
    if(complation){
       complation();
    }

    
}

5.实现方法:加载失败

- (void)LoadingFailComplation:(void (^)(void))complation{
    [self stopLoadingShapeLayerAnimation];
    promptLabel.text=@"加载失败";
    [self createReloadingButton];
    SPSelf;
    if(complation){
        weakSelf.failBlock = complation;
    }
    
}
-(void)createReloadingButton{
    reloadButton=[[UIButton alloc]initWithFrame:SPFrame(0, 0, 100, 30)];
    reloadButton.center=SPPoint(self.frame.size.width/2, self.frame.size.height/2+50);
    [reloadButton setTitle:@"重新加载" forState:UIControlStateNormal];
    [reloadButton setTitleColor:[UIColor orangeColor] forState:UIControlStateNormal];
    reloadButton.backgroundColor=[UIColor whiteColor];
    reloadButton.layer.cornerRadius=14;
    reloadButton.layer.masksToBounds=YES;
    reloadButton.layer.borderWidth=1;
    reloadButton.layer.borderColor=[UIColor orangeColor].CGColor;
    reloadButton.titleLabel.font=SPFont(14);
    [reloadButton addTarget:self action:@selector(reloadEvent) forControlEvents:UIControlEventTouchUpInside];
    [self addSubview:reloadButton];
    
}
/*
 *重新加载按键点击事件*
 */
-(void)reloadEvent{
    [reloadButton removeFromSuperview];
    [self loadingShapeLayerAnimation];
    self.failBlock();
    
}

6.动画实现

/*
 *load圈转动*
 */
-(void)loadingShapeLayerAnimation{
    CABasicAnimation*rotate=[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotate.fromValue=0;
    rotate.toValue=@(M_PI*2);
    rotate.duration=3;
    rotate.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    rotate.repeatCount=HUGE;
    rotate.fillMode=kCAFillModeForwards;
    rotate.removedOnCompletion=NO;
    [_loadingShapeLayer addAnimation:rotate forKey:rotate.keyPath];
    
}

评论(0) 浏览(1062)

轮播图的封装

2019-4-26 王建伟 iOS开发

新产品项目中需要编写一个轮播图的功能,效果如下图所示:

其中我们需要用到一个第三方库:iCarousel,这是一个做轮播图非常强大的库,基本可以满足日常开发的需求,我们将在此库的基础上编写实现我们需要的轮播图

首先建立一个基础与UIView的子类:NSRoastView

需要2个属性,1.NSTimer用来实现图片的自动轮播;2.iCarousel第三方库

初始化

实现iCarousel代理方法

实现方法让他能自动轮播

评论(0) 浏览(1339)

推荐一个原型制作工具墨刀

2019-4-19 王建伟 iOS开发

最近一周都是在商量新产品的功能和制作简易的产品原型,这边我用的墨刀来进行原型的制作

在墨刀,你既可以创建移动端项目(iPhone/Android)、平板项目、也可以创建Web/电视项目、Watch项目,并可以自定义尺寸,为各种屏幕创建原型。

登陆墨刀之后,点击“新建项目”即可。

当然,也可以选择“从模板中创建项目”。

鼠标悬浮在模板上方,点击“使用模板”,就能创建该模板项目。

创建好项目后,进入到工作区

1.添加页面

添加同级页面:选中一个页面,再点击页面列表顶部的「添加新页面」,便可以新建该页面的同级页面。

添加子级页面:鼠标悬浮某页面上,点击「...」展开更多按钮,单击「添加子页面」即可为该页面添加子级页面。目前最多支持创建7级页面。

2.修改页面名称

双击修改页面名称:可使用“数字”+“页面功能”方式命名,既便于页面识别,又可弥补多级列表演示时的不足,更清晰地展示页面逻辑关系。<> /p

3.调整页面顺序

拖拽调整页面顺序:拖动某页面在页面列表上下移动位置即可。如果将页面 A 移动为页面 B 的次级页面,直接拖拽 A 至 B 区域处即可。

4.复制/转移/删除页面

鼠标悬浮某页面,页面名称右侧会显示「...」更多按钮,点击更多按钮展开「创建副本」、「移动到」以及「删除」的按钮。

5.编辑页面式样

在右侧的“页面设置面板”,你可以选择页面是“竖屏”还是“横屏”演示,可以修改背景颜色。

可以选择是否显示“布局”,并可以修改“列数”/"间隔"/“尺寸”。

可以选择是否显示“网格”,并可以调节网格大小。

可以查看当前页面链接数,并在这里一键删除。

6.添加组件和图标

在最右的设置面板左侧里有“组件”、“我的组件”、“图标”库,点击即可打开相应的素材库。

组件(官方组件)库:墨刀官方提供丰富的组件库,除了基础组件,还有苹果的 iOS, 谷歌的Material design, 微信的 WeUI,蚂蚁金服的 Ant Design, Windows 10 等多套组件库。这些都可以直接使用,画原型效率大大提升。

我的组件:你自定义的组件库。

图标:覆盖各领域的图标,来自 Font Awesome, Material Design 和墨刀图标系列。

墨刀目前有两种方式添加组件/图标到画布上:

1 双击组件/图标

2 拖拽组件/图标到画布

对于左侧(在“推荐”模式下)的“快捷组件”来说,还有第三种添加方式:按住对应的快捷键然后鼠标画出。比如画“长方形”组件,就按住“R”。至于其他组件用什么快捷键,鼠标悬浮在“快捷组件”上就可以看到啦!

7.编辑组件属性

选中组件,右侧便会呈现出组件的设置面板。

在面板里可以编辑组件的各种属性,比如位置、大小、颜色、透明度、阴影、动效等等。

选中多个组件,可以编辑它们的布局方式,比如“左对齐”、“右对齐”、“居中对齐“、“水平等间距”、“垂直等间距”…

利用好这些功能就能简单的绘制一个原型满足日常需求了

标签: 原型制作工具简单的功能介绍

评论(0) 浏览(1897)

runtime的一些其他常见用法

2019-4-12 王建伟 iOS开发

上篇我们利用runtime来实现了按键点击范围的扩大,其实runtime还有许多常见用法,比如:动态获取类名,动态获取类的成员变量,动态获取类的属性列表,动态获取类的方法列表,动态获取类所遵循的协议列表,动态添加新的方法,类的实例方法实现的交换,动态属性关联,消息发送与消息转发机制等。下面就介绍一些常见的用法

1.获取类名

动态的获取类名是比较简单的,使用class_getName(Class)就可以在运行时来获取类的名称。class_getName()函数返回的是一个char类型的指针,也就是C语言的字符串类型,所以我们要将其转换成NSString类型,然后再返回出去。下方的+fetchClassName:方法就是我们封装的获取类名的方法

2.获取成员变量

下方这个+fetchIvarList:这个方法就是我们封装的获取类的成员变量的方法。当然我们在获取成员变量时,可以用ivar_getTypeEncoding()来获取相应成员变量的类型。使用ivar_getName()来获取相应成员变量的名称。下方就是对获取成员变量的功能的封装。返回的是一个数组,数组的元素是一个字典,而字典中存储的就是相应成员变量的名称和类型。

3.获取成员属性

上面获取的是类的成员变量,那么下方这个+fetchPropertyList:获取的就是成员属性。当然此刻获取的只包括成员属性,也就是那些有setter或者getter方法的成员变量。下方主要是使用了class_copyPropertyList(Class,&count)来获取的属性列表,然后通过for循环通过property_getName()来获取每个属性的名字。当然使用property_getName()获取到的名字依然是C语言的char类型的指针,所以我们还需要将其转换成NSString类型,然后放到数组中一并返回。

4.获取类的实例方法

接下来我们就来封装一下获取类的实例方法列表的功能,下方这个+fetchMethodList:就是我们封装的获取类的实例方法列表的函数。在下方函数中,通过class_copyMethodList()方法获取类的实例方法列表,然后通过for循环使用method_getName()来获取每个方法的名称,然后将方法的名称转换成NSString类型,存储到数组中一并返回。

5.动态添加方法实现

下方就是动态的往相应类上添加方法以及实现。下方的+addMethod方法有三个参数,第一个参数是要添加方法的类,第二个参数是方法的SEL,第三个参数则是提供方法实现的SEL。稍后在消息发送和消息转发时会用到下方的方法。下方主要是使用class_getInstanceMethod()和method_getImplementation()这两个方法相结合获取相应SEL的方法实现。下方的IMP其实就是Implementation的方法缩写,获取到相应的方法实现后,然后再调用class_addMethod()方法将IMP与SEL进行绑定即可。

6.方法实现交换

下方就是讲类的两个方法的实现进行交换。如果将MethodA与MethodB的方法实现进行交换的话,调用MethodA时就会执行MethodB的内容,反之亦然。

评论(0) 浏览(970)

利用runtime实现按键点击范围放大

2019-4-4 王建伟 iOS开发

在编写项目的过程中我们一般都会遇到这样的情况,按键设计的比较小,用户不方便点击,造成体验很差这种状况,我们就需要在不改变按键UI的情况下实现按键点击范围放大

这时候我们就需要用的runtime机制,在程序运行和编译的时候做一个小动作,改变一下按键的点击范围

首先我们先建立一个button的分类来扩充一下按键的方法

设定一些静态的常量

static char topNameKey

static char rightNameKey

static char bottomNameKey

static char leftNameKey

然后编写扩大范围的方法

返回一个扩大的范围

改写按键点击事件,让点击范围变大

评论(0) 浏览(921)

数据存储方式FMDB

2019-3-29 王建伟 iOS开发

FMDB说iOS平台的SQLite数据库框架,使用起来更加面向对象,省去了很多麻烦,冗余的C语言代码,对比苹果自带的Core Data框架,更加轻量级和灵活,提供了多线程安全的数据库操作方法,有效的防止数据混乱

FMDB有三个主要的类

FMDatabase:一个FMDatabase对象就代表一个单独的SQLite数据库,用来执行SQL语句

FMResultSet:使用FMDatabase执行查询后的结果集

FMDatabaseQueue:用于在多线程执行多个查询或更新,它是线程安全的

打开数据库,通过指定SQLite数据库文件路径来创建FMDatabase对象

FMDatabase*db=[FMDatabase databaseWithPath:path];

if(![db open]){NSLog(@"数据库打开失败");}

文件路径有三种情况

(1) 具体文件路径,如果不存在会自动创建

(2) 空字符串@“”,会在临时目录创建一个空的数据库,当FMDatabase连接关闭时,数据库文件也被删除

(3) nil会创建一个内存中临时数据库,当FMDatabase连接关闭时,数据库会被销毁

在FMDB中,除查询以外的所有操作,都被称为“更新” create drop insert update delete等

使用executeUpdate:方法执行更新

-(BOOL)executeUpdate:(NSString*)sql

-(BOOL)executeUpdateWithFormat:(NSString*)format,...

-(BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray*)arguments

查询方法

-(FMResultSet*)executeQuery:(NSString*)sql,...

-(FMResultSet*)executeQueryWithFormat:(NSString*)format,...

-(FMResultSet*)executeQuery:(NSString*)sql withArgumentInArray:(NSArray*)arguments

遍历结果集

while([rs next]){NSString*name = [rs stringForColumn:@"name"];int age=[rs intForColumn:@"age"];double score = [rs doubleForColumn:@"score"];}

示例代码

评论(0) 浏览(927)

数据存储方式NSUserDefaults

2019-3-22 王建伟 iOS开发

很多iOS应用都支持偏好设置,比如保存用户账号,密码等,iOS提供了一套标准等解决方案来为应用加入偏好设置功能,每个应用都有个NSUserDefaults实例,通过它来存取偏好设置

保存数据

读取数据

补充说明

偏好设置是专门用来保存应用程序等配置信息的,一般情况不要在偏好设置中保存其他数据。如果利用偏好设置来存储数据,默认就是存储在Preferences文件夹下面的,偏好设置会将所有的数据都存储到同一个文件中

使用偏好设置对数据进行保存之后,它保存到系统的时间是不确定的,会在将来某个时间点自动将数据保存到Preferences文件夹下面,如果需要即刻将数据存储,可以使用[defaults synchronize]

所有的信息都写在一个文件中,对比简单的plist可以保存和读取基本的数据类型

评论(0) 浏览(2598)

网站备案号:京ICP备11043289号-1 北京市公安局网络备案 海1101084571
版权所有 北京育灵童科技发展有限公司 Copyright © 2002-2024 www.elight.cn, All Rights Reserved