怎么确定电子设备1060 音频输出怎么样的质量

让你加速学习的5个步骤
超简单的时间精力管理法,让你的2017不再颓废
我喜欢一座城
因为弱,就活该被骂?
30岁后,你会站在哪里?
实现梦想没那么伟大,80%的人只是输给了半途而废
思考生活的意义并不能帮我们改变世界,但是可以帮我们认识自己。
一些让人眼前一亮的句子您好,欢迎回来
您好,欢迎来到中国供应商!
镀金3.5音频线可伸缩AUX插头转接线公对公耳机1米厂家直销现货
镀金3.5音频线可伸缩AUX插头转接线公对公耳机1米厂家直销现货
50 - 99999条
≥100000条
马先生经理
手机已验证
发货地广东 深圳
发货期限不限
供货总量0条
手机已验证
经营模式|经销批发
企业类型|其他
公司地址|广东 深圳 福田区 中国 广东 深圳市福田区 华发北路电子科技大厦四楼B座4132
查看全部分类
本页信息为深圳市东凯电子有限公司为您提供的""产品信息,如您想了解更多关于"镀金3.5音频线可伸缩AUX插头转接线公对公耳机1米厂家直销现货"价格、型号、厂家请联系厂家,或给厂家留言。
品牌DK接口3.5适用机型通用型号iPhone 4,iPhone 5
镀金3.5音频线可伸缩AUX插头转接线公对公耳机1米厂家直销现货
镀金3.5mm音频AUX插头转接线公对公耳机1米伸缩线工厂直销现货产品名称:3.5mm音频AUX伸缩音频线产品颜色:白色、黑色产品接口:3.5mm 音频 AUX公对公功 能:音频数据高保真传输;线  长:80CM线  材:镀金纯铜线质适用产品:适用于苹果手机iPhone4、IPhone5、E音箱、平板电脑等电子设备的音频输出;产品包装:100条/包,500条/箱。本品特点:1.本产品为出口产品,适用于大部分数码设备带有3.5mm音频插孔的手机,IPHONE,MP3,PSP,GPS等带有AUX插口的音频连接,可以与车载音响播放器相连接。作为播放音乐之用。2.本品线体柔软、弯曲性好,抗干扰性能强,传输信号效果好,使用效果佳。便于摆放和收纳,不会有长线带来的缠绕,信号的损失。3.AUX就是音频输入接口,可以从包括MP3/手机/IPHONE/导航仪等在内的电子声频设备输出音频(接口一般为3.5mm耳机插孔),目的是用车上的音响听这些设备的音乐。1)输入孔:就是你汽车音响的AUX IN的插孔。 这个插头一般是3.5mm的孔径。 所以汽车绝大部分基本上是通用的。2)输出孔:就是你的MP3,MP4,MP5,GPS导航仪,其他播放器的音频输出孔。这类的插孔一般都是3.5mm标准孔。如果你想把手机转到汽车AUX孔,这样就要先确认手机的插孔是不是3.5mm.适用车型范围:(原车有AUX插孔都是可以用的)改装前置CD播放器:SONY/先锋/JVC等带有AUX输入插孔。使用说明:1.将本品的一头插入你的输出设备,如MP3,MP3,MP5,GPS等。打开输出设备。2.另一头插入车内的AUX插孔,打开车上AUX开关。3.调节音量,这样就可以听到属于你自己的音乐了。【联系方式】深圳市东凯电子有限公司联系人:马先生电话:&151 && 5阿里旺旺:msk9016 &QQ: 【下单付款方式】1、付款方式:预付30%款,余款发货后付清.(异地需款到发货);2、以上价格为不含税、不含特殊包装价格;3、以上价格为工厂交货价格,不含运费,报价有效期在1个月内4、如果有现货数量达到要求可立即发货。5、如不了解产品,请联系发样品,确认后下单,质量没问题可下单,交货如果&&&& 产品与样品有差异可支持退换货,质量都以样品为准!【售后说明】&关于退换货1.不同产品,都有相应的质保期,在质保期内如出现质量问题,非人为损坏的情况下,可以更换新的.2.买家接到货时间验货,如出现严重质量问题需要退货及时与我司工作人员联系解决处理问题逾期不可退货.(退货的前提必须是产品外观和包装要保持完好、清洁,不得有损坏或磨损的迹象。)&&&&3.如非本商行发错货或者产品本身质量存在严重质量问题的,一律不可退货。4.产品属于人为损坏,包装损破,超过质保期的,一律不可退换货。&&&关于维修如产品出现质量问题,没超出质保期内(非人为损坏的情况下),买家可以以累积的方,等坏品累计到了一定的数量之后再一次性发回来换,换好的产品可以在需要订货的时候再连同订购的货品一起发回去。(本商行不负责产品返修的来回运费,客户需自付。)祝http://detail.1688.com/offer/.html您:购物愉快!!!&
供应商信息
深圳市东凯电子有限公司专业生产通讯电子设备,主要生产手机充电器、数据线、车充、高档充电器等手机配套产品。公司位于广东省深圳市宝安区,拥有得天独厚的地理环境和良好的成产氛围。
公司本着“安全、高效、敬业、精业”的方针,经过多年的创业发展,目前已拥有一流的生产设备和专业的技术力量。用心尽力做优质产品,诚信以质创一流品牌。这是我们公司的经营理念,本公司从投产以来,始终要求广大员工严格遵守安全生产操作规程,严把质量关,建立了一套完整、可持续改进的质量管理体系。产品采取独特的设计,从电子元件的采购再到成品组装、测试、老化、包装、出货一条龙生产,秉承薄利多销的经营方式,在产品方面可以做到品质好、单价低、交期快、服务好等优势,随着市场的需求和工厂的发展,不断开发新的产品,产品质量经久不衰,日产量不断提高,品种、规格不断更新,品种档次不断升高。目前我们“东凯电子”电子有限分公司已进入高端充电器的行列并远销世界各地。
我公司坚持以“把技术创新当做是企业的灵魂,把产品质量当做是企业的生命”为宗旨,放眼世界,努力开拓国内和国外市场,以诚信为本。并用热情的双手随时欢迎四海宾客,希望能同国内外新老客户精诚合作,共谋发展大业
公司注册时间
公司所在地
广东 深圳 福田区 中国 广东 深圳市福田区 华发北路电子科技大厦四楼B座4132
主营产品或服务
马先生经理
地址广东 深圳 福田区 中国 广东 深圳市福田区 华发北路电子科技大厦四楼B座4132
广东 深圳 福田区 中国 广东 深圳市福田区 华发北路电子科技大厦四楼B座4132
深圳市东凯电子有限公司
北京亦创国际会展中心
深圳会展中心
南京国际博览中心
上海世博展览馆
深圳会展中心
上海新国际博览中心
免责声明:
本页面所展现的 镀金3.5音频线可伸缩AUX插头转接线公对公耳机1米厂家直销现货 信息及其他相关推荐信息,均来源于其对应的商铺,信息的真实性、准确性和合法性由该信息的来源商铺所属企业完全负责。中国供应商对此不承担任何保证责任。
友情提醒:
建议您在购买相关产品前务必确认供应商资质及产品质量,过低的价格有可能是虚假信息,请谨慎对待,谨防欺诈行为。
建议您在搜索产品时,优先选择信贸通会员,信贸通会员为中国供应商VIP会员,信誉度更高。
深圳市东凯电子有限公司
地址:广东 深圳 福田区 中国 广东 深圳市福田区 华发北路电子科技大厦四楼B座4132
技术支持:
按拼音检索:
主办单位:中国互联网新闻中心版权所有 中国互联网新闻中心中国供应商(www.china.cn)
成功收藏此产品
图片验证码
最小订货量
我想了解贵公司产品价格方面的问题,请尽快和我联系。
您采购的商品镀金3.5音频线可伸缩AUX插头转接线公对公耳机1米厂家直销现货
您的手机号码
提交联系方式,获取供应商最低报价
马先生 / 经理
深圳市东凯电子有限公司
*联系我们时请说明来自中国供应商!
您的手机号码
填写手机号接收企业名片
图片验证码
*联系我们时请说明来自中国供应商!音频输出设备是什么,怎么安装的 啊_百度知道
音频输出设备是什么,怎么安装的 啊
我电脑重装了下系统我就发现我电脑就没声音了,右下脚跳出来个窗口说没有可用的音频输出设备,怎么办啊,我急啊
我有更好的答案
你装的360安全卫士没啊?打开后点系统修复在点常规修复,我就是这样修好的,希望可以帮到你~
采纳率:39%
那你电脑重新安装系统看看,应该就有声音了。
你需要安装一个音频驱动程序
下载一个驱动人生吧
为您推荐:
其他类似问题
音频输出的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。修改返回按钮:
UIImage *backImage = [UIImageimageNamed:@&navback&]; & & [[UIBarButtonItemappearance]setBackButtonBackgroundImage:[backImageresizableImageWithCapInsets:UIEdgeInsetsMake(0,
backImage.size.width,0,0)] & & & & & & & & & & & & & & & & & & &
& & & & & & & &forState:UIControlStateNormalbarMetrics:UIBarMetricsDefault];
& & [[UIBarButtonItemappearance]setBackButtonTitlePositionAdjustment:UIOffsetMake(-233,0)forBarMetrics:UIBarMetricsDefault];
tableview快速创建:
#pragma mark-tableview delegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath
*)indexPath{
& & return80;
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
& & return0;
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
& & return0;
#pragma mark-datasource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
& & return10;
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
& & return1;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath{
& & UITableViewCell* cell=[[UITableViewCellalloc]init];
& & return
uicollectiobview
&UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayoutalloc]init];
& & layout.itemSize & & & & & & =CGSizeMake(myscreenwith/8.0,64);
& & layout.footerReferenceSize& =CGSizeMake(0,0);
& & layout.sectionInset& & & & & & =UIEdgeInsetsMake(0,0,0,0);
& & layout.minimumInteritemSpacing =0.0;
& & layout.minimumLineSpacing& & & =0.0;
& & _week=[[UICollectionViewalloc]initWithFrame:CGRectMake(0,0,320,568)collectionViewLayout:layout];
#pragma mark -- UICollectionViewDataSource
//定义展示的UICollectionViewCell的个数
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
& & return30;
//定义展示的Section的个数
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
& & return1;
//每个UICollectionView展示的内容
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath
*)indexPath
& & staticNSString * CellIdentifier =@&GradientCell&;
& & UICollectionViewCell * cell = [collectionViewdequeueReusableCellWithReuseIdentifier:CellIdentifierforIndexPath:indexPath];
& & cell.backgroundColor = [UIColorcolorWithRed:((10
* indexPath.row) /255.0)green:((20 * indexPath.row)/255.0)blue:((30
* indexPath.row)/255.0)alpha:1.0f];
& & return
#pragma mark --UICollectionViewDelegateFlowLayout
//定义每个UICollectionView的大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath
& & returnCGSizeMake(96,100);
//定义每个UICollectionView的 margin
-(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout
*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
& & returnUIEdgeInsetsMake(5,5,5,5);
#pragma mark --UICollectionViewDelegate
//UICollectionView被选中时调用的方法
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath
*)indexPath
& & UICollectionViewCell * cell = (UICollectionViewCell *)[collectionViewcellForItemAtIndexPath:indexPath];
& & cell.backgroundColor = [UIColorwhiteColor];
//返回这个UICollectionView是否可以被选择
-(BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath
*)indexPath
& & returnYES;
滑动事件:
//滑动开始事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
& & UITouch *touch = [touchesanyObject];
& & CGPoint pointone = [touchlocationInView:self.view];//获得初始的接触点
& & startPoint& =
//滑动移动事件
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
& & UITouch *touch = [touchesanyObject];
& & //imgViewTop是滑动后最后接触的View
& & CGPoint pointtwo = [touchlocationInView:self.view];&//获得滑动后最后接触屏幕的点
& & if(fabs(pointtwo.x-startPoint.x)&100)
& & {& //判断两点间的距离
& & & & bMove =YES;
//滑动结束处理事件
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
& & UITouch *touch = [touchesanyObject];
& & pointtwo = [touchlocationInView:self.view];&//获得滑动后最后接触屏幕的点
& & if((fabs(pointtwo.x-startPoint.x)&20)&&(bMove))
& & & & //判断点的位置关系左滑动
& & & & if(pointtwo.x-startPoint.x&0)
& & & & { & //左滑动业务处理
& & & & & & NSLog(@&左滑、、、、、&);
& & & & //判断点的位置关系右滑动
& & & & else
& & & & {& //右滑动业务处理
& & & & & & NSLog(@&右滑、、、、、&);
//-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
//& & CGPoint point1=[_container convertPoint:point fromView:self];
//& & //& & if ([_container pointInside:point1 withEvent:event]) {
//& & //& & & &
//& & //& & }
//& & return _
&//统一修改导航栏字体
& & [[UINavigationBarappearance]setTitleTextAttributes:@{NSFontAttributeName:[UIFontsystemFontOfSize:17],
& & & & & & & & & & & & & & & & & & & & & & & & & & & & &NSForegroundColorAttributeName:[UIColorcolorWithRed:51/255.0green:51/255.0&blue:51/255.0&alpha:1.0]}];
& & //修改系统返回键按钮样式
& & UIImage *backImage = [UIImageimageNamed:@&navback&];
& & [[UIBarButtonItemappearance]setBackButtonBackgroundImage:[backImageresizableImageWithCapInsets:UIEdgeInsetsMake(0,
backImage.size.width,0,0)] & & & & & & & & & & & & & & & & & & &
& & & & & & & &forState:UIControlStateNormalbarMetrics:UIBarMetricsDefault];
& & [[UIBarButtonItemappearance]setBackButtonTitlePositionAdjustment:UIOffsetMake(-233,0)forBarMetrics:UIBarMetricsDefault];
& & [[UIBarButtonItemappearance]setTitleTextAttributes:@{NSFontAttributeName:[UIFontsystemFontOfSize:15]}forState:UIControlStateNormal];
修改导航栏与状态栏颜色
&self.navigationController.navigationBar.barTintColor
=[UIColor colorWithRed:24/255.0green:179/255.0blue:139.0/255.0alpha:1.0];
How Do I Declare A Block in Objective-C?
As a&local variable:
returnType&(^blockName)(parameterTypes)
= ^returnType(parameters) {...};
As a&property:
@property (nonatomic, copy)&returnType&(^blockName)(parameterTypes);
As a&method parameter:
- (void)someMethodThatTakesABlock:(returnType&(^)(parameterTypes))blockName;
As an&argument to a method call:
[someObject someMethodThatTakesABlock:^returnType&(parameters)
As a&typedef:
typedef&returnType&(^TypeName)(parameterTypes);
TypeName&blockName = ^returnType(parameters) {...};
This site is not intended to be an exhaustive list of all possible uses of blocks.
If you find yourself needing syntax not listed here, it is likely that a&typedef&would make your code more readable.
iOS8开发中采用xcassets设置LaunchImage后如何获取自适应屏幕大小的启动图片
NSDictionary*dic=@{@&320x480&:@&LaunchImage-700&,@&320x568&:@&LaunchImage-700-568h&,@&375x667&:@&LaunchImage-800-667h&,@&414x736&:@&LaunchImage-800-Portrait-736h&};
NSString*key=[NSString
stringWithFormat:@&%dx%d&,(int)[UIScreenmainScreen].bounds.size.width,(int)[UIScreenmainScreen].bounds.size.height];
UIImage*launchImage=[UIImage
imageNamed:dict[key]];
导航栏去掉下面的黑线
if ([self.navigationController.navigationBar respondsToSelector:@selector( setBackgroundImage:forBarMetrics:)]){
NSArray *list=self.navigationController.navigationBar.subviews;
for (id obj in list) {
if ([obj isKindOfClass:[UIImageView class]]) {
UIImageView *imageView=(UIImageView *)
NSArray *list2=imageView.subviews;
for (id obj2 in list2) {
if ([obj2 isKindOfClass:[UIImageView class]]) {
UIImageView *imageView2=(UIImageView *)obj2;
imageView2.hidden=YES;
git commands
是否每次敲block都要在心里默念: 没有返回值~括号~尖号~参数...不用这么麻烦, 试着敲个inline,还没等你敲完,人家xcode已经给你提示了,
然后你负责敲个回车:
哈哈哈,这需要记?
把block去typedef也是一样的.直接敲typedef...选择typedefBlock那一项:
一个回车之后请做填空题:
写枚举的时候总是不小心写个#号或者@,又或者不小心忘了应该先写typedef还是enum, 不要担心, 从此你只需要敲enumdef,如:
回车之后就是见证奇迹的时刻:
使用GCD定时器的时候,输入dispatch_source后查找Xcode内置代码段
GCD定时器使用的快捷输入
Xcode会帮你做好GCD定时器一系列的创建、设置属性、回调函数、启动等工作.意外收获的惊喜呀,所以赶紧补上来了.
GCD定时器快捷创建
拖动tableView时收起键盘
只有一行代码:
tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
其中keyboardDismissMode是UIScrollView的属性,
它的值除了UIScrollViewKeyboardDismissModeNone,
还有一种是UIScrollViewKeyboardDismissModeInteractive,表示键盘可以随着手指下滑而移出屏幕,具体效果小伙伴们可以自己试着体验下.
既然是UIScrollView的属性,那么在文字常用的UITextView等控件中也可以使用.
我们可以通过Quartz 2D来绘制文本内容
绘制文本内容
- (void)drawRect:(CGRect)rect{
NSString *str = @&苹果在昨天成功获得一项关于音频方面的专利,可以让纤薄电子设备中的扬声器音量更高,音质更好。虽然该专利的文件内容冗长,不过却描述了一种方法:“由于便携式电子设备的体积限制,在使设备更薄更小的同时,提供高质量的音频输出/接收能力已变得愈发困难。因此,就需要新的方法,来提供高质量的音频输出/接收能力。” &;
CGRect rect = CGRectMake(20, 50, 374, 500);
UIFont *font = [UIFont systemFontOfSize:25];
UIColor *color = [UIColor redColor];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc]init];
NSTextAlignment textAlignment = NSTextAlignmentCenter;
paragraphStyle.alignment = textA
[str drawInRect:rect withAttributes:@{NSFontAttributeName:font, NSForegroundColorAttributeName:color, NSParagraphStyleAttributeName:paragraphStyle}];
- (void)drawRect:(CGRect)rect{
UIImage *image = [UIImage imageNamed:@&mv1.jpg&];
[image drawInRect:CGRectMake(20, 300, 374, 400)];
同样我们可以通过Quartz 2D进行图片剪切
- (void)drawRect:(CGRect)rect{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(context, CGRectMake(80, 50, 100, 100));
CGContextClip(context);
UIImage *image = [UIImage imageNamed:@&mv2.jpg&];
[image drawAtPoint:CGPointMake(50, 50)];
coretext里面设置富文本:
NSString*string =@&Be
Bold! And a little color wouldn’t hurt either.&;NSDictionary*attrs
= @{NSFontAttributeName:
[UIFontsystemFontOfSize:36]};
NSMutableAttributedString*
as = [[NSMutableAttributedStringalloc]initWithString:stringattributes:attrs];
[as addAttribute:NSFontAttributeNamevalue:[UIFontboldSystemFontOfSize:36]
range:[stringrangeOfString:@&Bold!&]];
[as addAttribute:NSForegroundColorAttributeNamevalue:[UIColorblueColor]
range:[stringrangeOfString:@&little
[as addAttribute:NSFontAttributeNamevalue:[UIFontsystemFontOfSize:18]
range:[stringrangeOfString:@&little&]];self.label.attributedText=
NSMutableAttributedString*as = ...
[as enumerateAttribute:...inRange:...options:...
CGContextMoveToPoint设置起点
CGContextClosePath 连接起点和当前点
CGPathCreateMutable 类似于 CGContextBeginPath
CGPathMoveToPoint 类似于 CGContextMoveToPoint
CGPathAddLineToPoint 类似于 CGContextAddLineToPoint
CGPathAddCurveToPoint 类似于 CGContextAddCurveToPoint
CGPathAddEllipseInRect 类似于 CGContextAddEllipseInRect
CGPathAddArc 类似于 CGContextAddArc
CGPathAddRect 类似于 CGContextAddRect
CGPathCloseSubpath 类似于 CGContextClosePath
CGContextAddPath函数把一个路径添加到graphics
&dispatch_group_t group = dispatch_group_create();& & dispatch_group_enter(group);& & [RecipeInfo fetchRecipeWithCompletionBlock:^(id returnValue) {& & & & info = [RecipeInfo yy_modelWithDictionary:returnValue];& & & & dispatch_group_leave(group);& & } WithFailureBlock:^(NSError *error) {& & & & dispatch_group_leave(group);& & }];&& && & dispatch_group_enter(group);& & [HomePageNavContent fetchNavContentWithCompletionBlock:^(id returnValue) {& & & & navContent = [HomePageNavContent yy_modelWithDictionary:returnValue];& & & & dispatch_group_leave(group);& & } WithFailureBlock:^(NSError *error) {& & & & dispatch_group_leave(group);& & }];
& & dispatch_group_notify(group, dispatch_get_main_queue(), ^{& & & & [self.tableView reloadData];& & });
1. 网络图片显示大体步骤:下载图片图片处理(裁剪,边框等)写入磁盘从磁盘读取数据到内核缓冲区从内核缓冲区复制到用户空间(内存级别拷贝)解压缩为位图(耗cpu较高)如果位图数据不是字节对齐的,CoreAnimation会copy一份位图数据并进行字节对齐CoreAnimation渲染解压缩过的位图以上4,5,6,7,8步是在UIImageView的setImage时进行的,所以默认在主线程进行(iOS UI操作必须在主线程执行)。2. 一些优化思路:异步下载图片image解压缩放到子线程使用缓存 (包括内存级别和磁盘级别)存储解压缩后的图片,避免下次从磁盘加载的时候再次解压缩减少内存级别的拷贝 (针对第5点和第7点)良好的接口(比如SDWebImage使用category)Core Data vs
图片预下载
有时候使用UITableView所实现的列表,会使用到section,但是又不希望它粘在最顶上而是跟随滚动而消失或者出现,下面的代码片段就是实现此功能:
- (void)scrollViewDidScroll:(UIScrollView*)scrollView{&&
&&&&if(scrollView==_tableView){&&
&&&&&&&&CGFloatsectionHeaderHeight=36;
&&&&&&&&if(scrollView.contentOffset.y&=sectionHeaderHeight&&scrollView.contentOffset.y&=0){&&
&&&&&&&&&&&&scrollView.contentInset=UIEdgeInsetsMake(-scrollView.contentOffset.y,0,0,0);&&
&&&&&&&&}elseif(scrollView.contentOffset.y&=sectionHeaderHeight){&&
&&&&&&&&&&&&scrollView.contentInset=UIEdgeInsetsMake(-sectionHeaderHeight,0,0,0);&&
&&&&&&&&}&&
UITableView的分割线默认是开头空15像素点的(好像是15来着~~),产品经理有时候希望能够定格显示,那么你可能会这么做。
self.tableView.separatorInset = UIEdgeInsetsZero;
但是你很快就会发现这么做并没有效果,这是因为separatorInset这个属性在iOS7以后就已经失效了,但是我们还是能够达到同样的效果,你可以在你的tablevView的代理协议实现界面加上下面这段代码:
-(void)viewDidLayoutSubviews
if ([self.tableView respondsToSelector:@selector(setSeparatorInset:)]) {
[self.tableView setSeparatorInset:UIEdgeInsetsMake(0,0,0,0)];
if ([self.tableView respondsToSelector:@selector(setLayoutMargins:)]) {
[self.tableView setLayoutMargins:UIEdgeInsetsMake(0,0,0,0)];
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
[cell setSeparatorInset:UIEdgeInsetsZero];
if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
[cell setLayoutMargins:UIEdgeInsetsZero];
CFAttributedStringRef :属性字符串,用于存储需要绘制的文字字符和字符属性
CTFramesetterRef:通过CFAttributedStringRef进行初始化,作为CTFrame对象的生产工厂,负责根据path创建对应的CTFrame
CTFrame:用于绘制文字的类,可以通过CTFrameDraw函数,直接将文字绘制到context上
CTLine:在CTFrame内部是由多个CTLine来组成的,每个CTLine代表一行
CTRun:每个CTLine又是由多个CTRun组成的,每个CTRun代表一组显示风格一致的文本
实际上CoreText是不直接支持绘制图片的,但是我们可以先在需要显示图片的地方用一个特殊的空白占位符代替,同时设置该字体的CTRunDelegate信息为要显示的图片的宽度和高度,这样绘制文字的时候就会先把图片的位置留出来,再在drawRect方法里面用CGContextDrawImage绘制图片。
-&(void)drawRect:(CGRect)rect&{
&&&&[super&drawRect:rect];&&&&//&省略步骤1-4&&,步骤8
&&&&//&步骤9:图文混排部分
&&&&//&CTRunDelegateCallbacks:一个用于保存指针的结构体,由CTRun&delegate进行回调
&&&&CTRunDelegateCallbacks&
&&&&memset(&callbacks,&0,&sizeof(CTRunDelegateCallbacks));
&&&&callbacks.version&=&kCTRunDelegateVersion1;
&&&&callbacks.getAscent&=&ascentC
&&&&callbacks.getDescent&=&descentC
&&&&callbacks.getWidth&=&widthC&&&&
&&&&//&图片信息字典
&&&&NSDictionary&*imgInfoDic&=&@{@&width&:@100,@&height&:@30};&&&&
&&&&//&设置CTRun的代理
&&&&CTRunDelegateRef&delegate&=&CTRunDelegateCreate(&callbacks,&(__bridge&void&*)imgInfoDic);&&&&
&&&&//&使用0xFFFC作为空白的占位符
&&&&unichar&objectReplacementChar&=&0xFFFC;&&&&NSString&*content&=&[NSString&stringWithCharacters:&objectReplacementChar&length:1];&&&&NSMutableAttributedString&*space&=&[[NSMutableAttributedString&alloc]&initWithString:content];&&&&CFAttributedStringSetAttribute((CFMutableAttributedStringRef)space,&CFRangeMake(0,&1),&kCTRunDelegateAttributeName,&delegate);&&&&CFRelease(delegate);&
&&&&//&将创建的空白AttributedString插入进当前的attrString中,位置可以随便指定,不能越界
&&&&[attrString&insertAttributedString:space&atIndex:50];&&&&
&&&&//&省略步骤5-6
&&&&//&步骤10:绘制图片
&&&&UIImage&*image&=&[UIImage&imageNamed:@&coretext-img-1.png&];&&&&CGContextDrawImage(context,&[self&calculateImagePositionInCTFrame:frame],&image.CGImage);&&&
&&&&//&省略步骤7
&#pragma&mark&-&CTRun&delegate&回调方法
&static&CGFloat&ascentCallback(void&*ref)&{&&&&
&&&&return&[(NSNumber&*)[(__bridge&NSDictionary&*)ref&objectForKey:@&height&]&floatValue];
&}&static&CGFloat&descentCallback(void&*ref)&{&&&&
&&&&return&0;
&static&CGFloat&widthCallback(void&*ref)&{&&&&
&&&&return&[(NSNumber&*)[(__bridge&NSDictionary&*)ref&objectForKey:@&width&]&floatValue];
&*&&根据CTFrameRef获得绘制图片的区域
&*&&@param&ctFrame&CTFrameRef对象
&*&&@return绘制图片的区域
&-&(CGRect)calculateImagePositionInCTFrame:(CTFrameRef)ctFrame&{&&&&
&&&&//&获得CTLine数组
&&&&NSArray&*lines&=&(NSArray&*)CTFrameGetLines(ctFrame);&&&&NSInteger&lineCount&=&[lines&count];&&&&CGPoint&lineOrigins[lineCount];
&&&&CTFrameGetLineOrigins(ctFrame,&CFRangeMake(0,&0),&lineOrigins);&&&&
&&&&//&遍历每个CTLine
&&&&for&(NSInteger&i&=&0&;&i&&&lineC&i++)&{
&&&&&&&&CTLineRef&line&=&(__bridge&CTLineRef)lines[i];&&&&&&&&NSArray&*runObjArray&=&(NSArray&*)CTLineGetGlyphRuns(line);&&&&&&&&
&&&&&&&&//&遍历每个CTLine中的CTRun
&&&&&&&&for&(id&runObj&in&runObjArray)&{
&&&&&&&&&&&&
&&&&&&&&&&&&CTRunRef&run&=&(__bridge&CTRunRef)runO&&&&&&&&&&&&NSDictionary&*runAttributes&=&(NSDictionary&*)CTRunGetAttributes(run);
&&&&&&&&&&&&CTRunDelegateRef&delegate&=&(__bridge&CTRunDelegateRef)[runAttributes&valueForKey:(id)kCTRunDelegateAttributeName];&&&&&&&&&&&&if&(delegate&==&nil)&{&&&&&&&&&&&&&&&&
&&&&&&&&&&&&}&&&&&&&&&&&&
&&&&&&&&&&&&NSDictionary&*metaDic&=&CTRunDelegateGetRefCon(delegate);&&&&&&&&&&&&if&(![metaDic&isKindOfClass:[NSDictionary&class]])&{&&&&&&&&&&&&&&&&
&&&&&&&&&&&&}&&&&&&&&&&&&
&&&&&&&&&&&&CGRect&runB&&&&&&&&&&&&CGFloat&&&&&&&&&&&&&CGFloat&
&&&&&&&&&&&&
&&&&&&&&&&&&runBounds.size.width&=&CTRunGetTypographicBounds(run,&CFRangeMake(0,&0),&&ascent,&&descent,&NULL);
&&&&&&&&&&&&runBounds.size.height&=&ascent&+&&&&&&&&&&&&&
&&&&&&&&&&&&CGFloat&xOffset&=&CTLineGetOffsetForStringIndex(line,&CTRunGetStringRange(run).location,&NULL);
&&&&&&&&&&&&runBounds.origin.x&=&lineOrigins[i].x&+&xO
&&&&&&&&&&&&runBounds.origin.y&=&lineOrigins[i].y;
&&&&&&&&&&&&runBounds.origin.y&-=&&&&&&&&&&&&&
&&&&&&&&&&&&CGPathRef&pathRef&=&CTFrameGetPath(ctFrame);&&&&&&&&&&&&CGRect&colRect&=&CGPathGetBoundingBox(pathRef);&&&&&&&&&&&&
&&&&&&&&&&&&CGRect&delegateBounds&=&CGRectOffset(runBounds,&colRect.origin.x,&colRect.origin.y);&&&&&&&&&&&&return&delegateB
&&&&}&&&&return&CGRectZ
注意到,在使用Notifikation的时候,会需要声明字符串常量,作为notification的name。这时,const的位置就比较重要,很容易让不了解的人犯错误:错误的写法(常量指针):1
extern const NSString * RNFooDidCompleteNotification;
正确的写法(指针常量):1
extern NSString * const RNFooDidCompleteNotification;
这里涉及到常量指针和指针常量的概念,简单的来说:常量指针:就是指向常量的指针,关键字 const 出现在 * 左边,表示指针所指向的地址的内容是不可修改的,但指针自身可变。指针常量:指针自身是一个常量,关键字 const 出现在 * 右边,表示指针自身不可变,但其指向的地址的内容是可以被修改的。在此例中:我们知道,NSString永远是immutable的,所以NSString * const 是有效的,而const NSString * 则是无效的。而使用错误的写法,则无法阻止修改该指针指向的地址,使得本应该是常量的值能被修改,造成了隐患。这是需要注意的一个常见错误。HTTP缓存,明确的要知道GET请求可以被缓存,POST不能被缓存,所以要想在客户端做HTTP的缓存一定要注意使用GET请求!头域的概念:一图胜千言下图中的右边栏加粗字体都是头域:Cache,Client,Entity,Transport等等fiddler-get.png再看左栏的Result也就是状态码:可以看到我这是304:这代码当前页面在本地的缓存没有过期和服务器一致,可以使用!那这是怎么做到的呢?有下面两种方式。If-Modified-Since/Last-ModifiedIf-Modified-Since这个是在Request里面的Cache中的信息用来表示本地缓存最后一次被修改的时间,他被发送到服务器并且和Response的Entity中Last-Modified作比较,如果两者的日期一致,那就说明在此期间页面没有任何改动浏览器可以使用本地缓存。(所提到的头域都可以在上面图中找到,大家结合图来看比较清晰)If-None-Match/EtagIf-None-Match是在Request中请求头的第一行,他存储一个字符串(资源在服务器的唯一确定标志)。Etag是Response中Entity中的一个字符串。两个也是做比较,相同说明可以使用缓存。http协商缓存中:Etag/lastModified完整过程(可以配合上面的HTTP流程理解):1.客户端请求一个页面(A)。2.服务器返回页面A,并在给A加上一个Last-Modified/ETag。3.客户端展现该页面,并将页面连同Last-Modified/ETag一起缓存。4.客户再次请求页面A,并将上次请求时服务器返回的Last-Modified/ETag一起传递给服务器。5.服务器检查该Last-Modified或ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304和一个空的响应体。那么问题来了为什么要使用两种缓缓存方式呢?而且从我的截图中可以看到仅有第二种Etag方式原因如下:1.如果一些资源定期生成,这种情况下内容没有变化但是服务器的 Last-Modified改变了,导致文件使用不了缓存。2.Last-Modified的日期只能精确到秒从上面截图可以看到,如果在1s内做了修改,那么就会出现误判3.由于If-Modified-Since/Last-Modified这种方式使用时间判断一定要保证服务器和本地的时间的一致。Etag是资源在服务器的唯一标识符,能够更加准确的控制缓存。Last-Modified 与 ETag 是可以一起使用的,但是服务器会优先验证 ETag,在一致之后才会判断Last-Modified,判断是否返回304.修正:当两者同时存在的时候,通过以下表达式来进行缓存策略的判断.感谢@我在睡觉if ETagFromServer != ETagOnClient || LastModifiedFromServer != LastModifiedOnClient
GetFromServer
GetFromCache参考: 使用Ctrl+F5 强制刷新页面,可以忽略以上讲的两种缓存策略操作Cache-ControlLast-Modified/Etag前进后退有效有效刷新-F5无效有效强制刷新无效无效缓存过期时间Expires:到期时间。作用: 浏览器会在指定过期时间内使用本地缓存你可以设置这个时间为Sun, 17-Jan-:07 GMT,因为这个时间是32位unix支持的最大的时间值,就可以做到缓存的东西一直有效。如果将这个值设置成-1那么缓存将会立即失效。如果没有设置Expires对于firefox失效时间=0.1*(Time-Last-Modified ),上一次时间的0.1的时间后失效。这样设计很有道理月九没修改越久的保存效果。如果10没有修改1天后就会失效。
怎么改变uitextfield placeholder的颜色和位置?
继承uitextfield,重写这个方法
- (void) drawPlaceholderInRect:(CGRect)rect { [[UIColor blueColor] setFill]; [self.placeholder drawInRect:rect withFont:self.font lineBreakMode:UILineBreakModeTailTruncation alignment:self.textAlignment];}
1. 应用还没有加载
这时如果点击通知的显示按钮,会调用didFinishLaunchingWithOptions,不会调用didReceiveRemoteNotification方法。
如果点击通知的关闭按钮,再点击应用,只会调用didFinishLaunchingWithOptions方法。
2. 应用在前台(foreground)
这时如果收到通知,会触发didReceiveRemoteNotification方法。
3.应用在后台
(1)此时如果收到通知,点击显示按钮,会调用didReceiveRemoteNotification方法。
(2)点击关闭再点击应用,则上面两个方法都不会被调用这时,只能在applicationWillEnterForeground或者applicationDidBecomeActive,根据发过来通知中的badge进行判断是否有通知,然后发请求获取数据
navigationBar的透明
有时候,我们需要将navigationBar设置透明,但不是隐藏,因为还需要其item控件(返回键什么的),虽然navigationBar是继承于UIView的,但是直接设置其alpha是无效的,应该是因为navigationBar复合的视图层级:
根据视图层级关系,我们用这个十分简单的方法来设置navigationBar的透明:
[[[self.navigationController.navigationBar subviews] objectAtIndex:0] setAlpha:0];
一个图层可以有一个和它相关联的 mask(蒙板),mask 是一个拥有 alpha 值的位图,当像素要和它下面包含的像素合并之前都会把 mask 应用到图层的像素上去。当你要设置一个图层的圆角半径时,你可以有效的在图层上面设置一个 mask。但是也可以指定任意一个蒙板。比如,一个字母 A 形状的 mask。最终只有在 mask 中显示出来的(即图层中的部分)才会被渲染出来。
CGAffineTransform transForm = self.buttonView.transform;
self.buttonView.transform = CGAffineTransformTranslate(transForm, 10, 0);
CGAffineTransform transForm = self.buttonView.transform;
self.buttonView.transform = CGAffineTransformRotate(transForm, M_PI_4);
self.buttonView.transform = CGAffineTransformScale(transForm, 1.2, 1.2);
self.buttonView.transform = CGAffineTransformIdentity;
1、 三角函数   double sin (double);正弦   double cos (double);余弦   double tan (double);正切   2 、反三角函数   double asin (double); 结果介于[-PI/2, PI/2]   double acos (double); 结果介于[0, PI]   double atan (double); 反正切(主值), 结果介于[-PI/2, PI/2]   double atan2 (double, double); 反正切(整圆值),
结果介于[-PI, PI]   3 、双曲三角函数   double sinh (double);   double cosh (double);   double tanh (double);   4 、指数与对数   double exp (double);求取自然数e的幂   double sqrt (double);开平方   double log (double); 以e为底的对数   double log10 (double);以10为底的对数   double pow(double x, double
y);计算以x为底数的y次幂   float powf(float x, float y); 功能与pow一致,只是输入与输出皆为浮点数   5 、取整   double ceil (double); 取上整   double floor (double); 取下整   6 、绝对值   double fabs (double);求绝对值   double cabs(struct complex znum) ;求复数的绝对值   7 、标准化浮点数   double frexp (double f, int
*p); 标准化浮点数, f = x * 2^p, 已知f求x, p ( x介于[0.5, 1] )   double ldexp (double x, int p); 与frexp相反, 已知x, p求f   8 、取整与取余   double modf (double, double*); 将参数的整数部分通过指针回传, 返回小数部分   double fmod (double, double); 返回两参数相除的余数   9 、其他   double hypot(double x, double y);已知直角三角形两个直角边长度,求斜边长度  double
ldexp(double x, int exponent);计算x*(2的exponent次幂)   double poly(double x, int degree, double coeffs [] );计算多项式   nt matherr(struct exception *e);数学错误计算处理程序
更新:最后提供的所谓“终极”解决方案,之前都是自己的项目在用,分享出来之后,发现有一些地方还需要改进。但是总体思路不变,因此如非必要文章不会做大幅更改,最终代码请以文末github地址为准,另,欢迎提供Bug
前一段时间换了工作,公司项目赶得比较紧,没有时间更新文章,现在闲下来了,赶紧写一篇来弥补自己的羞愧。
今天我们来重点讨论导航栏返回的问题,包括各种问题的解决方案。
系统默认导航栏的返回按钮和返回方式
在默认情况下,导航栏返回按钮长这个样子
导航栏默认返回按钮
导航栏左上角的返回按钮,其文本默认为上一个ViewController的标题,如果上一个ViewController没有标题,则为Back(中文环境下为“返回”)。
在默认情况下,导航栏返回的点击交互和滑动交互如下
默认导航栏交互
这些东西不需要任何设置和操作,因此也没有其他需要说明的地方。
自定义左上角的返回按钮
绝大多数情况下,我们都需要根据产品需求自定义左上角的返回按钮,虽然这对大多数开发者来说不是什么难事,但依然有几个问题值得注意。
替换左上角返回按钮
替换返回按钮非常简单,只需要在ViewController中创建一个UIBarButtonItem和一张图片,并为按钮添加相应的点击事件即可,代码如下
- (void)viewDidLoad {
[super viewDidLoad];
UIButton * leftBtn = [UIButton buttonWithType:UIButtonTypeSystem];
leftBtn.frame = CGRectMake(0, 0, 25,25);
[leftBtn setBackgroundImage:[UIImage imageNamed:@&nav_back&] forState:UIControlStateNormal];
[leftBtn addTarget:self action:@selector(leftBarBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:leftBtn];
- (void)leftBarBtnClicked:(UIButton *)btn
[self.navigationController popViewControllerAnimated:YES];
我们来看一眼效果
替换返回按钮
调整按钮位置
我们可以看到,上面的按钮是有点偏右的,那如果我们想调整按钮的位置该怎么做呢?设置Frame显然是行不通的,因为导航栏的NavigationItem是个比较特殊的View,我们无法通过简单的调整Frame来的调整左右按钮的位置。但是在苹果提供的UIButtonBarItem中有个叫做UIBarButtonSystemItemFixedSpace的控件,利用它,我们就可以轻松调整返回按钮的位置。具体使用方法如下
UIButton * leftBtn = [UIButton buttonWithType:UIButtonTypeSystem];
leftBtn.frame = CGRectMake(0, 0, 25,25);
[leftBtn setBackgroundImage:[UIImage imageNamed:@&icon_back&] forState:UIControlStateNormal];
[leftBtn addTarget:self action:@selector(leftBarBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem * leftBarBtn = [[UIBarButtonItem alloc]initWithCustomView:leftBtn];;
UIBarButtonItem * spaceItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
spaceItem.width = -15;
self.navigationItem.leftBarButtonItems = @[spaceItem,leftBarBtn];
我们来看一眼效果
调整返回按钮位置
可以看到,我们的返回按钮已经紧靠着屏幕边缘。
这个方法同样适用于调整导航栏右侧的按钮
让滑动返回手势生效
如果使用自定义的按钮去替换系统默认返回按钮,会出现滑动返回手势失效的情况。解决方法也很简单,只需要重新添加导航栏的interactivePopGestureRecognizer的delegate即可。
首先为ViewContoller添加UIGestureRecognizerDelegate协议
然后设置代理
self.navigationController.interactivePopGestureRecognizer.delegate = self;
至此,我们已经将返回按钮替换为我们的自定义按钮,并使滑动返回重新生效。接下来,我们继续来解决交互上的问题。
全屏滑动返回
这个一个很常见的需求,网上解决方案也很多,这里将本人常用的方法贴到这里。仅供参考
实现全屏滑动返回仅需在导航栏给导航栏添加UIGestureRecognizerDelegate协议,并在ViewDidLoad中写入如下代码
id target = self.interactivePopGestureRecognizer.
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:@selector(handleNavigationTransition:)];
pan.delegate = self;
[self.view addGestureRecognizer:pan];
self.interactivePopGestureRecognizer.enabled = NO;
我们来看一眼效果(注意鼠标位置)
全屏滑动返回.gif
这种方法的原理其实很简单,其实就是自定义一个全屏滑动手势,并将滑动事件设置为系统滑动事件,然后禁用系统滑动手势即可。handleNavigationTransition就是系统滑动的方法,虽然系统并未提供接口,但是我们我们可以通过runtime找到这个方法,因此直接调用即可。两位,不必担心什么私有API之类的问题,苹果如果按照方法名去判断是否使用私有API,那得误伤多少App。
NavigationBar切换动画的“终极解决方案”
本部分文字代码都较多,不想看这么多废话的同学请直接翻到末尾,文末附有下载地址,导入项目后,继承即可生效。
在改变了导航栏样式,实现了全屏滑动返回之后,我们有了一个看起来还不错的导航栏。但是我们滑动时的切换依然是系统自带的动画,如果遇到前一个界面的NavigationBar为透明或前后两个Bar颜色不一样,这种渐变式的动画看起来就会不太友好,尤其当前后两个界面其中一个界面的NavigationBar为透明或隐藏时,其效果更是惨不忍睹。
这个问题,其实很多App,比如天猫、美团等都通过一种“整体返回”的效果来解决这个问题。效果如下:
整体滑动返回
这种解决方案等于将两个NavigationBar独立开来,因此可以相对完美的解决导航栏滑动切换中的种种Bug。
接下来,我们来看看如何实现这种效果。
以我个人的认知,实现这个效果有三种基本思路:
使用UINavigationController自带的setNavigationBarHidden:
animated:方法来实现,每次push或pop时,在当前控制器的viewWillDisappear:中设置隐藏,在要跳转的控制器的viewWillAppear:中设置导航栏显示。在每次Push前对当前页面进行截图并保存到数组,Pop时取数组最后一个元素显示,滑动结束后调用系统Pop方法并删除最后一张截图。使用iOS 7之后开放的,UIViewControllerAnimatedTransitioning协议,来实现自定义导航栏转场动画及交互。
以上三种方法,方法一十分繁琐,而且会有很多莫名其妙的BUG,直接pass。
在iOS的交互中,push一般通过按钮的点击事件或View的tap事件触发,而pop则可能通过事件触发,也可能通过右滑手势触发。因此,我们将这个我们要实现的动画效果分为交互效果和无交互效果两种,下面我们将使用方法2和方法3提供的思路,分别实现这两种效果,这样就能较为完美的解决Push和Pop的动画问题。
实现交互动画效果
准备需要使用的数组及手势
#define ScreenWidth [UIScreen mainScreen].bounds.size.width
#define ScreenHeight [UIScreen mainScreen].bounds.size.height
@interface LTNavigationController ()&UIGestureRecognizerDelegate&
@property(strong,nonatomic)UIImageView * screenshotImgV
@property(strong,nonatomic)UIView * coverV
@property(strong,nonatomic)NSMutableArray * screenshotI
@property(strong,nonatomic)UIPanGestureRecognizer *panGestureR
@implementation LTNavigationController
- (void)viewDidLoad {
[super viewDidLoad];
_panGestureRec = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:self action:@selector(panGestureRec:)];
_panGestureRec.edges = UIRectEdgeLeft;
[self.view addGestureRecognizer:_panGestureRec];
_screenshotImgView = [[UIImageView alloc] init];
_screenshotImgView.frame = CGRectMake(0, 0, ScreenWidth, ScreenHeight);
_coverView = [[UIView alloc] init];
_coverView.frame = _screenshotImgView.
_coverView.backgroundColor = [UIColor blackColor];
_screenshotImgs = [NSMutableArray array];
实现手势的相应事件
- (void)panGestureRec:(UIPanGestureRecognizer *)panGestureRec
if(self.visibleViewController == self.viewControllers[0]) return;
switch (panGestureRec.state) {
case UIGestureRecognizerStateBegan:
[self dragBegin];
case UIGestureRecognizerStateEnded:
[self dragEnd];
[self dragging:panGestureRec];
#pragma mark 开始拖动,添加图片和遮罩
- (void)dragBegin
[self.view.window insertSubview:_screenshotImgView atIndex:0];
[self.view.window insertSubview:_coverView aboveSubview:_screenshotImgView];
_screenshotImgView.image = [_screenshotImgs lastObject];
#define kDefaultAlpha 0.6
#define kTargetTranslateScale 0.75
#pragma mark 正在拖动,动画效果的精髓,进行位移和透明度变化
- (void)dragging:(UIPanGestureRecognizer *)pan
CGFloat offsetX = [pan translationInView:self.view].x;
if (offsetX & 0) {
self.view.transform = CGAffineTransformMakeTranslation(offsetX, 0);
double currentTranslateScaleX = offsetX/self.view.frame.size.
if (offsetX & ScreenWidth) {
_screenshotImgView.transform = CGAffineTransformMakeTranslation((offsetX - ScreenWidth) * 0.6, 0);
double alpha = kDefaultAlpha - (currentTranslateScaleX/kTargetTranslateScale) * kDefaultA
_coverView.alpha =
#pragma mark 结束拖动,判断结束时拖动的距离作相应的处理,并将图片和遮罩从父控件上移除
- (void)dragEnd
CGFloat translateX = self.view.transform.
CGFloat width = self.view.frame.size.
if (translateX &= 40) {
[UIView animateWithDuration:0.3 animations:^{
self.view.transform = CGAffineTransformIdentity;
_screenshotImgView.transform = CGAffineTransformMakeTranslation(-ScreenWidth, 0);
_coverView.alpha = kDefaultA
} completion:^(BOOL finished) {
[_screenshotImgView removeFromSuperview];
[_coverView removeFromSuperview];
[UIView animateWithDuration:0.3 animations:^{
self.view.transform = CGAffineTransformMakeTranslation(width, 0);
_screenshotImgView.transform = CGAffineTransformMakeTranslation(0, 0);
_coverView.alpha = 0;
} completion:^(BOOL finished) {
self.view.transform = CGAffineTransformIdentity;
[_screenshotImgView removeFromSuperview];
[_coverView removeFromSuperview];
[self popViewControllerAnimated:NO];
实现截图保存功能,并在Push前截图
- (void)screenShot
UIViewController *beyondVC = self.view.window.rootViewC
CGSize size = beyondVC.view.frame.
UIGraphicsBeginImageContextWithOptions(size, YES, 0.0);
CGRect rect = CGRectMake(0, 0, ScreenWidth, ScreenHeight);
[beyondVC.view drawViewHierarchyInRect:rect
afterScreenUpdates:NO];
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
if (snapshot) {
[_screenshotImgs addObject:snapshot];
UIGraphicsEndImageContext();
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
if (self.viewControllers.count &= 1) {
[self screenShot];
[super pushViewController:viewController animated:YES];
重写常用的pop方法
在一开始基本原理地方,我们说过pop时要删除最后一张截图,用来保证数组中的最后一张截图是上一个控制器,但是很多情况下我们可能调用的是导航栏的popToViewController:
animated:方法或popToRootViewControllerAnimated:来返回,这种情况下,我们删除的可能就不是一张截图,因此我们需要分别重写这些Pop方法,去确定我们要删除多少张图片,代码如下
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
[_screenshotImgs removeLastObject];
return [super popViewControllerAnimated:animated];
- (NSArray&UIViewController *& *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
for (NSInteger i = self.viewControllers.count - 1; i & 0; i--) {
if (viewController == self.viewControllers[i]) {
[_screenshotImgs removeLastObject];
return [super popToViewController:viewController animated:animated];
- (NSArray&UIViewController *& *)popToRootViewControllerAnimated:(BOOL)animated
[_screenshotImgs removeAllObjects];
return [super popToRootViewControllerAnimated:animated];
※在指定的控制器屏蔽手势
在上面代码中,我们使用的是侧滑手势,并将相应区域设置为屏幕左侧。
之所以不用全屏滑动,是因为全屏滑动手势在有些时候会和其他手势冲突,如果冲突的是我们自定义的手势,自然好解决,但如果是系统手势,如TableView的左滑菜单操作,这个事情就很蛋疼的。
但是如果必须要做全屏滑动手势的话,我们可以对代码稍作修改,某些控制器中屏蔽手势。
首先给导航栏添加禁用名单数组并配置
@property(nonatomic,copy)NSArray * forbiddenA
- (void)viewDidLoad {
[super viewDidLoad];
self.panGestureRec.enabled = enable
self.forbiddenArray = @[@&SCViewController&,@&ManageAddressViewController&];
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
BOOL enable = YES;
for (NSString * string in self.forbiddenArray) {
NSString * className = NSStringFromClass([viewController class]);
if ([string isEqualToString:className]) {
enable = NO;
self.panGestureRec.enabled =
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
NSInteger count = self.viewControllers.
NSString * className = nil;
if (count &= 2) {
className = NSStringFromClass([self.viewControllers[count -2] class]);
BOOL enable = YES;
for (NSString * string in self.forbiddenArray) {
if ([string isEqualToString:className]) {
enable = NO;
self.panGestureRec.enabled =
return [super popViewControllerAnimated:animated];
到了这里,我们已经完成了交互式的切换动画,效果跟开头一样,就不再截图。接下来我们来解决另一个大Boss-非交互式动画
实现非交互动画效果
这里我们就要用到之前说的UIViewControllerAnimatedTransitioning来实现。限于篇幅,这里不再详细介绍这部分的基础知识,大家可以移步这两篇博客做一个初步的了解
注:FromVC代表即将消失的视图控制器,ToVC表示将要展示的视图控制器
我们要实现的效果:
Push的时候,FromVC往左移动,ToVC从屏幕右侧出现跟随FromVC左移直至FromVC消失,此时ToVC刚好完整显示在屏幕上。
Pop的时候,FromVC向右移动,ToVC从屏幕边缘出现跟随FromVC向右移动直至FromVC消失,此时ToVC刚好完整显示在屏幕上
实现的时候,我们依然需要将Push和Pop分开讨论
1.和交互式动画一样,每次Push时对屏幕截屏并保存,Pop的再次截屏但不保存
2.把Pop时截取的图片作为FromVC展示,把Push到这个界面时截取的图片作为ToVC展示
3.并对两张图片做位移动画,动画结束后移除两张图片
然后是Push
1.Push时先对当前屏幕截屏。
2.将截取的图片保存方便Pop回来时使用,并把这张图片作为这次Push的FromVC保存。
3.获取当前导航栏控制器对象,调整其Transform属性中的位移参数作为ToVC展示
4.对截图和导航栏做位移,动画结束后直接移除截屏图片
为什么要对导航栏作位移?
首先,在Push结束之前,我们是无法知道ToVC具体是什么样子,系统的截屏方法对于未加载出来的View是无能为力的,而UIView的&snapshotViewAfterScreenUpdates:方法又无法带着导航栏一起映射到一个新的View上,因此视觉效果很差。
正好在Pop的时候,为了达到想要的动画效果,用来展示的两张图片都需要放到导航栏的View上,因此在Push的时候我们就直接将导航栏的View做一个放射变换,当然,这也就意味着,当我们Push的时候,截屏就不能再放到导航栏上,而是应该放到它的“更上一层“ --&UITabbarController的View上
让我们撸一发代码
根据上述实现原理,我们可以知道,我们的主要工作重点在于打造一个合适的动画控制器。更准确的说,我们需要实现的细节都在UIViewControllerAnimatedTransitioning中,由于之前解释的很详细,这里我直接贴上相应代码供参考
-(void)animateTransition:(id&UIViewControllerContextTransitioning&)transitionContext
UIImageView * screentImgView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight)];
UIImage * screenImg = [self screenShot];
screentImgView.image =screenI
UIViewController * fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController * toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView * toView = [transitionContext viewForKey:UITransitionContextToViewKey];
CGRect fromViewEndFrame = [transitionContext finalFrameForViewController:fromViewController];
fromViewEndFrame.origin.x = ScreenW
CGRect fromViewStartFrame = fromViewEndF
CGRect toViewEndFrame = [transitionContext finalFrameForViewController:toViewController];
CGRect toViewStartFrame = toViewEndF
UIView * containerView = [transitionContext containerView];
if (self.navigationOperation == UINavigationControllerOperationPush) {
[self.screenShotArray addObject:screenImg];
[containerView addSubview:toView];
toView.frame = toViewStartF
UIView * nextVC = [[UIView alloc]initWithFrame:CGRectMake(ScreenWidth, 0, ScreenWidth, ScreenHeight)];
[self.navigationController.tabBarController.view insertSubview:screentImgView atIndex:0];
nextVC.layer.shadowColor = [UIColor blackColor].CGC
nextVC.layer.shadowOffset = CGSizeMake(-0.8, 0);
nextVC.layer.shadowOpacity = 0.6;
self.navigationController.view.transform = CGAffineTransformMakeTranslation(ScreenWidth, 0);
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
self.navigationController.view.transform = CGAffineTransformMakeTranslation(0, 0);
screentImgView.center = CGPointMake(-ScreenWidth/2, ScreenHeight / 2);
} completion:^(BOOL finished) {
[nextVC removeFromSuperview];
[screentImgView removeFromSuperview];
[transitionContext completeTransition:YES];
if (self.navigationOperation == UINavigationControllerOperationPop) {
fromViewStartFrame.origin.x = 0;
[containerView addSubview:toView];
if (_removeCount & 0) {
for (NSInteger i = 0; i & _removeC i ++) {
if (i == _removeCount - 1) {
lastVcImgView.image = [self.screenShotArray lastObject];
_removeCount = 0;
[self.screenShotArray removeLastObject];
lastVcImgView.image = [self.screenShotArray lastObject];
lastVcImgView.image = [self.screenShotArray lastObject];
screentImgView.layer.shadowColor = [UIColor blackColor].CGC
screentImgView.layer.shadowOffset = CGSizeMake(-0.8, 0);
screentImgView.layer.shadowOpacity = 0.6;
[self.navigationController.tabBarController.view addSubview:lastVcImgView];
[self.navigationController.tabBarController.view addSubview:screentImgView];
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
screentImgView.center = CGPointMake(ScreenWidth * 3 / 2 , ScreenHeight / 2);
lastVcImgView.center = CGPointMake(ScreenWidth/2, ScreenHeight/2);
} completion:^(BOOL finished) {
[lastVcImgView removeFromSuperview];
[screentImgView removeFromSuperview];
[self.screenShotArray removeLastObject];
[transitionContext completeTransition:YES];
- (void)removeLastScreenShot
[self.screenShotArray removeLastObject];
- (UIImage *)screenShot
UIViewController *beyondVC = self.navigationController.view.window.rootViewC
CGSize size = beyondVC.view.frame.
UIGraphicsBeginImageContextWithOptions(size, YES, 0.0);
CGRect rect = CGRectMake(0, 0, ScreenWidth, ScreenHeight);
[beyondVC.view drawViewHierarchyInRect:rect
afterScreenUpdates:NO];
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
注:removeLastScreenShot需要在使用滑动手势Pop后调用,用来清除动画控制器中保存的截图,否则当交互式和非交互式动画交替使用时,会出现截图混乱的问题。
在调用&popToViewController:(UIViewController *)viewController
animated:(BOOL)animated一次Pop多个页面,或调用popToRootViewControllerAnimated直接回到跟控制器时,一样需要清除对应数量的截图,并且需要和导航栏配合操作。新的代码已提交github,文章里也已经更新动画控制器对应的部分,具体代码还是以GitHub为准。
我们将动画持续时间调制两秒,观察一下效果
完成效果.gif
这篇文章开始于四个月之前,中间由于个人以及工作原因拖了又拖,终于在最近补完,逻辑混乱之处请见谅。
制作完成的导航栏和动画控制器的下载地址
使用方法:
1.将这四个文件导入工程
2.将需要动画的导航栏继承KLTNavigationController即可
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!}

我要回帖

更多关于 电信盒子怎么音频输出 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信