`
gkuiyj
  • 浏览: 91444 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

ios知识点总结

    博客分类:
  • iOS
 
阅读更多

转自:http://blog.csdn.net/sakulafly/article/details/40888143

 

1获取系统语言设置

 

     NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];

 

     NSArray *languages = [userDefault objectForKey:@"AppleLanguages"];

 

     NSString *preferredLang = [languages objectAtIndex:0];

 

2

 

缓存路径下文件大小

 

 

- (unsigned long long int) cacheFolderSize 

 

{

 

    NSFileManager  *_manager = [NSFileManager defaultManager];

 

    NSArray *_cachePaths =  NSSearchPathForDirectoriesInDomains(NSCachesDirectory,

                                                NSUserDomainMask, YES);

 

    NSString  *_cacheDirectory = [_cachePaths objectAtIndex:]; 

 

    NSArray  *_cacheFileList;

 

    NSEnumerator *_cacheEnumerator;

 

    NSString *_cacheFilePath;

 

    unsigned long long int _cacheFolderSize = ;

 

    _cacheFileList = [ _manager subpathsAtPath:_cacheDirectory];

 

   _cacheEnumerator = [_cacheFileList objectEnumerator];

 

    while (_cacheFilePath = [_cacheEnumerator nextObject]) 

 

   {

 

         NSDictionary *_cacheFileAttributes = [_managerfileAttributesAtPath:  

 

         [_cacheDirectory   stringByAppendingPathComponent:_cacheFilePath]

 

         traverseLink:YES];

 

      _cacheFolderSize += [_cacheFileAttributes fileSize];

 

    }

 

// 单位是字节

 

    return _cacheFolderSize;

 

}

 

3Popover push 时 Frame无法改变解决办法

 

在popover中的ViewController中实现:

 

- (void)viewWillAppear:(BOOL)animated 

{

 

   CGSize size = CGSizeMake(320, 480); // size of view in popover  

 

   self.contentSizeForViewInPopover = size; 

 

   [super viewWillAppear:animated]; 

 

}

 

4tableview滑动导致NSTimer和委托回调停止解决办法

 

/ /请求回调

 

NSURLRequest  * 请求  =  ...

 

scheduleInRunLoop :[ NSRunLoop  currentRunLoop ] 

                                            forMode :NSRunLoopCommonModes ] 

[ 连接开始] / /定时器回调

 

NSTimer * updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01f目标:自我选择:选择(updatePencent)的UserInfo:无重复:是];

 

* NSRunLoop主要= [NSRunLoop currentRunLoop] 

[主要addTimer:updateTimer forMode:NSRunLoopCommonModes];

 

5手势识别类

 

UIGestureRecognizer

 

 

6SFHFKeychainUtils 存储信息

 

苹果SDK自带的就有密码保护,使用方法很简单,如下:

 

1、引入Security.frameWork框架。

 

2、引入头文件:SFHKeychainUtils.h.

 

3、存密码:

 

[SFHFKeychainUtils storeUsername:@"dd" andPassword:@"aa"forServiceName:SERVICE_NAMEupdateExisting:1 error:nil];

 

[SFHFKeychainUtils deleteItemForUsername:@"dd" andServiceName:SERVICE_NAME error:nil];

 

4、取密码:

 

NSString *passWord =  [SFHFKeychainUtils getPasswordForUsername:@"dd"andServiceName:SERVICE_NAMEerror:nil];

 

7missing required architecture i386 in file 解决办法

 

在TargetInfo里面修改 Framework Search Pasths 删除里面内容就可以了。

 

 

8view 放大缩小动画效果

 

//创建缩小了的视图

myWeiBoImageVC = [[UIViewController alloc] init];

myWeiBoImageVC.view.clipsToBounds = YES;

myWeiBoImageVC.view.alpha = 0.0;

myWeiBoImageVC.view.frame = CGRectMake(64, 0, 1024-64, 768-20);

[self.view addSubview:myWeiBoImageVC.view];

    

CGAffineTransform newTransform = 

CGAffineTransformScale(myWeiBoImageVC.view.transform, 0.1, 0.1);

[myWeiBoImageVC.view setTransform:newTransform];

myWeiBoImageVC.view.center = CGPointMake(670, 100);

 

[self performSelector:@selector(imageViewControllerBigAnimation)];

 

//放大刚刚创建缩小后的视图

- (void)imageViewControllerBigAnimation{

   

    [UIView beginAnimations:@"imageViewBig" context:nil];

    [UIView setAnimationDuration:0.5];   

    CGAffineTransform newTransform =            CGAffineTransformConcat(myWeiBoImageVC.view.transform,  CGAffineTransformInvert(myWeiBoImageVC.view.transform));

    [myWeiBoImageVC.view setTransform:newTransform];

    myWeiBoImageVC.view.alpha = 1.0;

    myWeiBoImageVC.view.center = CGPointMake(416, 510);

    [UIView commitAnimations];

   

}

 

//缩小视图 隐藏

 

- (void)imageViewControllerSmallAnimation{

 

    [UIView beginAnimations:@"imageViewSmall" context:nil];

    [UIView setAnimationDuration:0.5];

    CGAffineTransform newTransform =  CGAffineTransformScale(myWeiBoImageVC.view.transform, 0.1, 0.1);

    [myWeiBoImageVC.view setTransform:newTransform];

    myWeiBoImageVC.view.center = CGPointMake(670, 100);

    [UIView commitAnimations];

   

}

 

9UIScrollView 控制View缩放

 

allImageScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 768, 1024)];

allImageScrollView.minimumZoomScale = 0.3;

allImageScrollView.maximumZoomScale = 1.0;

allImageScrollView.backgroundColor = [UIColor clearColor];

allImageScrollView.delegate = self;

[self.view addSubview:allImageScrollView];

 

mPicStatusesViewController = [[PicStatusesViewController alloc] init];

[allImageScrollView addSubview:mPicStatusesViewController.view];

 

//UIScrollView Delegete 实现

 

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView

 

{

    return mPicStatusesViewController.view; //返回ScrollView上添加的需要缩放的视图

}

 

- (void)scrollViewDidZoom:(UIScrollView *)scrollView

 

{

    //缩放操作中被调用

}

 

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale

 

{

    //缩放结束后被调用

  }

 

10、iOS3.2 播放视频

 

NSString *urlString = [NSString stringWithString:@"视频url"];

 

NSURL *movieUrl = [[NSURL alloc] initWithString:urlString];

    

MPMoviePlayerController *myMoviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:movieUrl];

myMoviePlayer.view.frame = CGRectMake(250, 250, 350, 350);

[self.view addSubview:myMoviePlayer.view];    

myMoviePlayer.shouldAutoplay = YES;

myMoviePlayer.scalingMode= MPMovieScalingModeAspectFit;  

[myMoviePlayer play];

 

 

11、谷歌地图翻起动画效果

 

    CATransition *animation = [CATransition animation];

    [animation setDelegate:self];

    [animation setDuration:0.35];

    [animation setTimingFunction:UIViewAnimationCurveEaseInOut];

    if (!curled){

 

        animation.type = @"pageCurl";

        animation.fillMode = kCAFillModeForwards;

        animation.endProgress = 0.40;

    } else {

        animation.type = @"pageUnCurl";

        animation.fillMode = kCAFillModeBackwards;

        animation.startProgress = 0.30;

    }

    [animation setRemovedOnCompletion:NO];

    [self.view exchangeSubviewAtIndex:0 withSubviewAtIndex:1];

    

    [self.view.layer addAnimation:animation forKey:@"pageCurlAnimation"];

 

12、给View添加阴影 和边框

 

UIImageView *imgvPhoto  = [UIImageView alloc] init];

 

//添加边框

   CALayer *layer = [_imgvPhoto layer];

    layer.borderColor = [[UIColor whiteColor] CGColor];

    layer.borderWidth = 5.0f;

//添加四个边阴影

    _imgvPhoto.layer.shadowColor = [UIColor blackColor].CGColor;

    _imgvPhoto.layer.shadowOffset = CGSizeMake(0, 0);

    _imgvPhoto.layer.shadowOpacity = 0.5;

    _imgvPhoto.layer.shadowRadius = 10.0;

//添加两个边阴影

    _imgvPhoto.layer.shadowColor = [UIColor blackColor].CGColor;

    _imgvPhoto.layer.shadowOffset = CGSizeMake(4, 4);

    _imgvPhoto.layer.shadowOpacity = 0.5;

    _imgvPhoto.layer.shadowRadius = 2.0;

 

13、使用NSTimer与UIView动画实现飘雪效果

 

viewDidLoad事件中,增加一个图片及定时器并启动,这里的pic请在头文件中定义。

 

-(void)viewDidLoad{

 [super viewDidLoad];

 self.pic = [UIImage imageNamed:@"snow.png"];//初始化图片

 //启动定时器,实现飘雪效果

 [NSTimer scheduledTimerWithTimeInterval:(0.2) target:self selector:@selector(ontime) userInfo:nil repeats:YES];

}

 

然后再实现定时器定时调用的ontime方法:

-(void)ontime{

 UIImageView *view = [[UIImageView alloc] initWithImage:pic];//声明一个UIImageView对象,用来添加图片

 view.alpha = 0.5;//设置该view的alpha为0.5,半透明的

 int x = round(random()20);//随机得到该图片的x坐标

 int y = round(random()20);//这个是该图片移动的最后坐标x轴的

 int s = round(random())+10;//这个是定义雪花图片的大小

 int sp = 1/round(random()0)+1;//这个是速度

 view.frame = CGRectMake(x, -50, s, s);//雪花开始的大小和位置

 [self.view addSubview:view];//添加该view

 [UIView beginAnimations:nil context:view];//开始动画

 [UIView setAnimationDuration:10*sp];//设定速度

 view.frame = CGRectMake(y, 500, s, s);//设定该雪花最后的消失坐标

 [UIView setAnimationDelegate:self];

 [UIView commitAnimations];

}

 

14、配置Xcode 看程序崩溃信息

 

1、在xcode中的左侧目录中找到Executables 打开

 

2、双击和工程名一样的文件。

 

3、在打开的文件中的Arguments选项,在下面的框中加入Name: NSZombieEnabled 设置value为YES。

 

15、程序中发送邮件和检测设备邮箱是否被配置

 

-(void)addEmail{

 

Class mailClass = (NSClassFromString(@"MFMailComposeViewController"));

 

if (mailClass != nil){

 

    if ([mailClass canSendMail]){

 

        [self displayComposerSheet];

 

    }else{

 

        [self launchMailAppOnDevice];

 

    }

 

}else{

 

    [self launchMailAppOnDevice];

 

    }

 

}

 

-(void)displayComposerSheet

 

{

 

MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init];

 

controller.navigationBar.tag = 1002;

 

[self.navigationController.navigationBar setNeedsDisplay];

 

controller.mailComposeDelegate = self;

 

[controller setSubject:@"意见反馈"];

 

[controller setToRecipients:[[NSArray alloc] initWithObjects:@"555@cifco.net.cn",nil]];

 

NSString *emailBody = nil;

 

[controller setMessageBody:emailBody isHTML:YES];

 

[self presentModalViewController:controller animated:YES];

 

[controller release];

 

}

 

#pragma mark mailComposeDelegate ----

 

- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error 

 

{

 

if (result == MFMailComposeResultSent) 

 

{

 

[self dismissModalViewControllerAnimated:YES];

 

}

 

if (result == MFMailComposeResultSaved) 

 

{

 

[self dismissModalViewControllerAnimated:YES];

 

}

 

if (result == MFMailComposeResultFailed) 

 

{

 

Emailalert = [[UIAlertView alloc] initWithTitle:@"" message:@"发送失败" delegate:selfcancelButtonTitle:@"知道了" otherButtonTitles:nil];

 

[Emailalert show];

 

}

 

if (result == MFMailComposeResultCancelled) 

 

{

 

[self dismissModalViewControllerAnimated:YES];

 

}

 

}

 

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex

 

{

 

if(alertView == Emailalert)

 

{

 

if (buttonIndex == ) 

 

{

 

[self dismissModalViewControllerAnimated:YES];

 

}

 

}else 

 

{

 

if (buttonIndex == ) 

 

{

 

//[self dismissModalViewControllerAnimated:YES];

 

}else 

 

{

 

NSString *recipients = @"mailto:theonelgq@gmail.com?cc=theone_liuguoqing@163.com&subject=text";

 

NSString *body = @"&body=text!";

 

NSString *email = [NSString stringWithFormat:@"%@%@", recipients, body];

 

email = [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

 

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:email]];

 

}

 

}

 

}

 

#pragma mark -

 

#pragma mark Workaround

 

-(void)launchMailAppOnDevice

 

{

 

isEmailalert = [[UIAlertView alloc] initWithTitle:@"警告" message:@"请配置您的邮箱" delegate:selfcancelButtonTitle:@"取消" otherButtonTitles:@"好的",nil];

 

[isEmailalert show];

 

}

 

16、程序启动画面大小

 

 iOS设备现在有三种不同的分辨率:iPhone 320x480、iPhone 4 640x960、iPad 768x1024。以前程序的启动画面(图片)只要准备一个 Default.png 就可以了,但是现在变得复杂多了。下面就是 CocoaChina 会员做得总结

 

  如果一个程序,既支持iPhone又支持iPad,那么它需要包含下面几个图片:

 

Default-Portrait.png iPad专用竖向启动画面 768x1024或者768x1004

 

Default-Landscape.png iPad专用横向启动画面 1024x768或者1024x748

 

Default-PortraitUpsideDown.png iPad专用竖向启动画面(Home按钮在屏幕上面),可省略 768x1024或者768x1004

 

Default-LandscapeLeft.png iPad专用横向启动画面,可省略 1024x768或者1024x748

 

Default-LandscapeRight.png iPad专用横向启动画面,可省略 1024x768或者1024x748

 

Default.png iPhone默认启动图片,如果没有提供上面几个iPad专用启动图片,则在iPad上运行时也使用Default.png(不推荐) 320x480或者320x460

 

Default@2x.png iPhone4启动图片640x960或者640x920

 

  为了在iPad上使用上述的启动画面,你还需要在info.plist中加入key: UISupportedInterfaceOrientations。同时,加入值UIInterfaceOrientationPortrait, UIInterfacOrientationPortraitUpsideDown, UIInterfaceOrientationLandscapeLeft, UIInterfaceOrientationLandscapeRight

 

17、ASIHTTPRequest实现断点下载

 

- (IBAction)URLFetchWithProgress:(id)sender

 

{

 

[startButton setTitle:@"Stop" forState:UIControlStateNormal];

 

[startButton addTarget:self action:@selector(stopURLFetchWithProgress:)forControlEvents:UIControlEventTouchUpInside];

 

NSString*tempFile = [[[[NSBundle mainBundle] bundlePath]stringByDeletingLastPathComponent]stringByAppendingPathComponent:@"MemexTrails_1.0b1.zip.download"];

 

if ([[NSFileManager defaultManager] fileExistsAtPath:tempFile]) {

 

[[NSFileManager defaultManager] removeItemAtPath:tempFile error:nil];

 

}

 

[self resumeURLFetchWithProgress:self];

 

}

 

- (IBAction)stopURLFetchWithProgress:(id)sender

 

{

 

networkQueue = [[ASINetworkQueue alloc] init];

 

timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:selfselector:@selector(updateBandwidthUsageIndicator) userInfo:nil repeats:YES];

 

timer = nil;

 

[startButton setTitle:@"Stop" forState:UIControlStateNormal];

 

[startButton addTarget:self action:@selector(URLFetchWithProgress:)forControlEvents:UIControlEventTouchUpInside];

 

[networkQueue cancelAllOperations];

 

[resumeButton setEnabled:YES];

 

}

 

- (IBAction)resumeURLFetchWithProgress:(id)sender 

 

{

 

[resumeButton setEnabled:NO];

 

[startButton setTitle:@"Start" forState:UIControlStateNormal];

 

 [startButton addTarget:self action:@selector(stopURLFetchWithProgress:)forControlEvents:UIControlEventTouchUpInside];

 

[networkQueue cancelAllOperations];

 

[networkQueue setShowAccurateProgress:YES];

 

[networkQueue setDownloadProgressDelegate:progressIndicator];

 

[networkQueue setDelegate:self];

 

[networkQueue setRequestDidFinishSelector:@selector(URLFetchWithProgressComplete:)];

 

ASIHTTPRequest*request=[[[ASIHTTPRequest alloc] initWithURL:[NSURLURLWithString:@"http://9991.net/blog/mp3/2.mp3"]] autorelease];

 

[request setDownloadDestinationPath:[[[[NSBundle mainBundle] bundlePath]

 

stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"MemexTrails_1.0b1.mp3"]];

 

[request setTemporaryFileDownloadPath:[[[[NSBundle mainBundle] bundlePath]stringByDeletingLastPathComponent]stringByAppendingPathComponent:@"MemexTrails_1.0b1.zip.down"]];

 

[request setAllowResumeForFileDownloads:YES];

 

[networkQueue addOperation:request];

 

[networkQueue go];

 

}

 

- (void)URLFetchWithProgressComplete:(ASIHTTPRequest *)request

 

{

 

if ([request error]) {

 

fileLocation.text=[NSString stringWithFormat:@"An error occurred:%@",[[[requesterror] userInfo] objectForKey:@"Title"]];

 

} else {

 

fileLocation.text=[NSString stringWithFormat:@"File downloaded to %@",[requestdownloadDestinationPath]];

 

}

 

[startButton setTitle:@"Start" forState:UIControlStateNormal];

 

[startButton addTarget:self action:@selector(URLFetchWithProgress:)forControlEvents:UIControlEventTouchUpInside];

 

}

 

- (IBAction)throttleBandwidth:(id)sender

 

{

 

if ([(UIButton *)sender state] ==YES) {

 

[ASIHTTPRequest setMaxBandwidthPerSecond:ASIWWANBandwidthThrottleAmount];

 

} else {

 

[ASIHTTPRequest setMaxBandwidthPerSecond:];

 

}

 

}

 

18、Safari 启动本地app

 

在plist文件中加入URL types 结构如下图,在Safari中地址栏输入 设置的字符串,比如设置的是

 

QQ,地址栏输入 QQ:// 就可以起点本地应用。

 

 

 

 

19、拖到视频进度与滑动手势冲突解决办法

 

#pragma mark -

#pragma mark UIGestureRecognizerDelegate

 

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch

{

    UIView *touchView = touch.view;

    

    if ([touchView isKindOfClass:[UISlider class]]) 

    {

        return NO;

    }

    else 

    {

        return YES;

    }

}

 

20、创建并保存Cookie的方法

 

 

        NSString *cookieString = [NSString stringWithString:[headers objectForKey:@"Cookie"]];

        

        NSMutableDictionary *cookieProperties = [[NSMutableDictionary alloc] init];

        [cookieProperties setValue:cookieString forKey:NSHTTPCookieValue];

        [cookieProperties setValue:@"QQCookie" forKey:NSHTTPCookieName];

        [cookieProperties setValue:@".QQ.com" forKey:NSHTTPCookieDomain];

        [cookieProperties setValue:[NSDate dateWithTimeIntervalSinceNow:60*60] forKey:NSHTTPCookieExpires];

        [cookieProperties setValue:@"/" forKey:NSHTTPCookiePath];

        NSHTTPCookie *newcookie = [[NSHTTPCookie alloc] initWithProperties:cookieProperties];

        

        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:newcookie];

 

 

21、popover横竖屏位置改变解决办法

 

1、 delegate中 处理

 

- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController

{

    userImageShow = NO;

    

    if ([popoverController isEqual:myPopover]) 

    {

        [myPopover release];

        myPopover = nil;

    }

}

 

2、屏幕旋转时重新弹出Popover

 

if (myPopover)  

 

{

 

     if ((self.interfaceOrientation == 

       UIInterfaceOrientationLandscapeLeft) || (self.interfaceOrientation == 

       UIInterfaceOrientationLandscapeRight)) 

     {

            [myPopover presentPopoverFromRect:CGRectMake(10,180, 1, 1)

                                                inView:self.view

                              permittedArrowDirections:UIPopoverArrowDirectionRight

                                              animated:YES];

     }

     else 

     {

           [myPopover presentPopoverFromRect:CGRectMake(20,180, 1, 1)

                                                inView:self.view

                              permittedArrowDirections:UIPopoverArrowDirectionRight

                                              animated:YES];

      }

 

}

 

22、plist各种key值含义

 

原文:http://www.minroad.com/?p=434

 

 

 

UIRequiresPersistentWiFi 在程序中弹出wifi选择的key(系统设置中需要将wifi提示打开)

UIAppFonts 内嵌字体(http://www.minroad.com/?p=412 有详细介绍)

UIApplicationExitsOnSuspend 程序是否在后台运行,自己在进入后台的时候exit(0)是很傻的办法

UIBackgroundModes 后台运行时的服务,具体看iOS4的后台介绍

UIDeviceFamily array类型(1为iPhone和iPod touch设备,2为iPad)

UIFileSharingEnabled 开启itunes共享document文件夹

UILaunchImageFile 相当于Default.png(更名而已)

UIPrerenderedIcon icon上是否有高光

UIRequiredDeviceCapabilities 设备需要的功能(具体点击这里查看)

UIStatusBarHidden 状态栏隐藏(和程序内的区别是在于显示Default.png已经生效)

UIStatusBarStyle 状态栏类型

UIViewEdgeAntialiasing 是否开启抗锯齿

CFBundleDisplayName app显示名

CFBundleIconFile、CFBundleIconFiles 图标

CFBundleName 与CFBundleDisplayName的区别在于这个是短名,16字符之内

CFBundleVersion 版本

CFBundleURLTypes 自定义url,用于利用url弹回程序

CFBundleLocalizations 本地资源的本地化语言,用于itunes页面左下角显示本地话语种

CFBundleDevelopmentRegion 也是本地化相关,如果用户所在地没有相应的语言资源,则用这个key的value来作为默认

最后附上官方文档,所有的key都有,看英文原版才是正路:)点我进入

 

24、xcode工程内添加多个Target

 

转自:http://blog.sina.com.cn/s/blog_682dc7810100pv8t.html

 

 

 

啥叫多Targets, 有啥用!

 

 

 

   相信很多人都注意到XCode中,

有个Target的概念.

    这在很多地方都有所体现,

比如打开一个工程后, 左侧的列表中有Targets一项, 而在工程界面的顶部菜单中,

project里面也有多个涉及到Target的项目,

那么这个Target到底是什么呢?  

    Apple的人是这样说的:

    

 

引用

 

Targets that define the products to build. A

target organizes the files and instructions needed to build a

product into a sequence of build actions that can be taken.

 

 

 

    简单的理解的话, 

 

可以认为一个target对应一个新的product(基于同一份代码的情况下). 但都一份代码了, 弄个新product做啥呢? 

 

折腾这个有意思么? 

 

    其实这不是单纯的瞎折腾, 

 

虽然代码是同一份, 但编译设置(比如编译条件), 以及包含的资源文件却可以有很大的差别. 于是即使同一份代码, 

 

产出的product也可能大不相同. 

 

    我们来举几个典型的应用多Targets的情况吧, 

 

比如完整版和lite版; 比如同一个游戏的20关, 30关, 50关版; 再或者比如同一个游戏换些资源和名字就当新游戏卖的(喂喂, 

 

你在教些什么...) 

 

 

Targets之间, 什么相同, 什么不同! 

 

 

    既然是利用同一份代码产出不同的product, 

 

那么到底不同Target之间存在着什么样的差异呢? 

 

    要解释这个问题, 

 

我们就要来看看一个Target指定了哪些内容. 

 

 

    从XCode左侧的列表中, 

 

我们可以看到一个Target包含了Copy Bundle Resources, Compile Sources, Link 

 

Binary With Libraries. 其中 

 

        Copy 

 

Bundle Resources 是指生成的product的.app内将包含哪些资源文件 

 

        Compile 

 

Sources 是指将有哪些源代码被编译 

 

        Link 

 

Binary With Libraries 是指编译过程中会引用哪些库文件 

 

 

    通过Copy 

 

Bundle Resources中内容的不同设置, 我们可以让不同的product包含不同的资源, 包括程序的主图标等, 

 

而不是把XCode的工程中列出的资源一股脑的包含进去. 

 

    而这还不是一个target所指定的全部内容. 

 

每个target可以使用一个独立, 

 

不同的Info.plist文件.   

 

    我们都知道, 

 

这个Info.plist文件内定义了一个iPhone项目的很多关键性内容, 比如程序名称, 

 

最终生成product的全局唯一id等等. 

 

     

 

    而且不同的target还可以定义完整的差异化的编译设置, 

 

从简单的调整优化选项, 到增加条件编译所使用的编译条件, 以至于所使用的base SDK都可以差异化指定. 

 

 

创建第二个Target! 

 

    为什么是第二个? 

 

因为第一个就是创建好工程后的默认Target呀! (废话这么多, 拖走...) 

 

 

    创建target有多种方法, 

 

我们可以从现有的target上复制出一份, 然后略加改动, 也可以完全新建一个target出来. 但其实说穿了, 

 

两个方法大同小异 

 

    首先我们来看看利用复制的方法创建target 

 

 

     利用复制创建target 

 

    我们在XCode左侧的列表中, 

 

展开 Targets 项, 在现有的target上, 右键选择 "Duplicate", 或者选中现有target后, 

 

在顶部菜单的Edit内选择"Duplicate"也可以. 

 

    此时我们就得到了一个新的target, 

 

而在Resource里面也会得到一个 xxxx copy.plist. 这个新的target与原有的target是完全一致的, 

 

余下的就是一些差异化的修改, 这个我们后面再说 

 

 

     创建全新的target 

 

    类似复制的方法, 

 

我们可以在左侧的列表中很多地方按下右键菜单, 都可以看到Add中会有"New Target..."一项, 

 

而在工程顶部菜单的Project内, 也可以看到这个"New Target..."的身影. 

 

    点击后, 

 

首先会让你选择target的类型, 既然我一直所指的都是程序本身, 那么自然选择Application了(至于其他的嘛, 

 

有兴趣的自己研究吧, 比如我们可以把程序中的部分提取成一个Static Library). 

 

    Next后, 

 

会让你输入一个新的Target的名字, 而不像复制的方法中, 默认生成 xxxxx copy这样的target名. 

 

    但是这样生成出的Target几乎是空的. 

 

Copy Bundle Resources, Compile Sources, Link Binary With 

 

Libraries里面都没有任何内容. 编译设置也是完全原始的状态. 

 

    可以通过拖拽内容到这些target的设置中, 

 

以及调整编译选项来完成Target的配置. 

 

 

 

Target中部分内容的修改方法! 

 

    其实这段的部分内容, 

 

在非多Targets的工程中也可能会用得到. 

 

    由于修改基本都是在工程/编译设置中完成, 

 

因此没有特殊情况, 就不再声明了, 打开target对应的工程/编译设置的方法可以采用在该target上右键, 选择get 

 

info来做到. 

 

 

    生成的product名称的修改: 

 

Packing段内的Product Name一项 

 

 

    Info.plist文件名: 

 

Packing段内的Info.plist File一项, 比如复制出来的target觉得那个xxxxx 

 

copy.plist太傻就可以在这里改 

 

 

    条 

 

件编译: 增加一个User-Defined Setting(Target "xxxx" 

 

Info的build页的左下角那个齿轮中可以看到这个内容), 在Other C Flag里面填入, 

 

比如要定义一个叫做LITE_VERSION的define值, 我们可以写上 "-DLITE_VERSION" 或 

 

"-DLITE_VERSION=1". 那么在程序中就可以用 

 

    #if 

 

defined(LITE_VERSION) 

 

    #else 

 

    #endif 

 

这样的条件编译来部分差异化代码了 

 

 

    也许有些朋友记得我在代码区贴过的检测破解版的代码, 

 

其中有一种检测方法就是看info.plist是文本还是二进制的, 那么我们能否建议一个模拟破解的target, 

 

直接生成文本的info.plist以便测试呢? 

 

    当然可以, 

 

在packing段内, 有一项叫"Info.plist Output Encoding", 默认值是Binary, 

 

我们只要选成xml, 那么生成出的product.app内的info.plist就直接是文本样式的了. 

 

 

 

    另 

 

外, 向Copy Bundle Resources, Compile Sources, Link Binary With 

 

Libraries内添加/删除文件, 可以在要改动的文件上, 选择get info, 并且切换到Target页, 

 

勾选要引用这个文件的target即可. 比如icon.png可以指定给默认target, 而icon_lite.png指定给lite 

 

verion的target 

 

 

 

大致就是如此吧, 懒得抓图了. 各位将就吧. 想到什么需要补充的, 我会加入 

 

另外 

 

一个英文教程:http://www.codza.com/free-iphone-app-version-from-the-same-xcode-project

 

25、详解IOS SDK兼容性引导

 

转自: http://mobile.51cto.com/iphone-284052.htm

 

 

 

IOS SDK兼容性引导是本文要介绍的内容,主要是基于IOS SDK基础的开发介绍说明如何应用于XCode工程的基于IOS SDK开发的技术。来看详细内容讲解。

 

1、用(weakly linked)弱连接类、方法和函数来支持在不同版本之间的程序运行

 

2、弱连接整个框架(framework)

 

3、为不同的IOS SDK选择不同的编译条件

 

4、在代码中找出过时API的使用

 

5、确定在运行时操作系统和框架(framework)的版本

 

一 、在IOS中使用弱连接类

 

在工程中使用类的弱连接的时候必须确保这些类在运行时的可用性,要不会引起动态连接的错误。

 

在IOS4.2以后的版本都是使用NSObject class的方法来检测弱连接在运行时态的可用性,这种简单高效的机制使用了NS_CLASS_AVAILABLE的可用性宏。

 

检测最近release的framework还不支持NS_CLASS_AVAILABLE的宏

 

在支持NS_CLASS_AVAILABLE的宏framework的条件编译中,可以如下的使用

 

if ([UIPrintInteractionController class]) {   

    // Create an instance of the class and use it.   

} else {   

    // Alternate code path to follow when the   

    // class is not available.   

如果你在不确保是否已经可以使用类方法的时候你可以使用NSClassFromString 方法来判断,使用方法如下:

 

Class cls = NSClassFromString (@"NSRegularExpression");   

if (cls) {   

    // Create an instance of the class and use it.   

} else {   

    // Alternate code path to follow when the   

    // class is not available.   

二、在方法,函数和符号中使用弱连接

 

和使用类的弱连接一样,在使用它之前要确保方法函数和符号在运行时的可用性,要不在编译的时候会报错动态连接错误,假设你想使用新版本IOS 

SDK的特性但是又想能够运行在低版本的SDK中,那么就要对早期的版本设置相应的开发target,在Object-c中 

instancesRespondToSelector: 

方法告诉我们所给的方法是否可用,例如:使用availableCaptureModesForCameraDevice:这个方法(在4.0以后才是可

用的),我们可以这样使用它。

 

1、检查一个Object-c方法的可用性

 

if ([UIImagePickerController instancesRespondToSelector:   

              @selector (availableCaptureModesForCameraDevice:)]) {   

    // Method is available for use.   

    // Your code can check if video capture is available and,   

    // if it is, offer that option.   

} else {   

    // Method is not available.   

    // Alternate code to use only still image capture.   

判断一个弱连接的C函数是否可用,只要判断函数的地址是否返回为NULL,以CGColorCreateGenericCMYK 函数为例,我们可以像以下那样使用。

 

2、检查C方法的可用性

 

if (CGColorCreateGenericCMYK != NULL) {   

    CGColorCreateGenericCMYK (0.1,0.5.0.0,1.0,0.1);   

} else {   

    // Function is not available.   

    // Alternate code to create a color object with earlier technology   

}  

要检测一个C方法是否可用,比较明确的为地址是否为NULL或零。你不能使用反运算符(!)来否定一个函数的可用性

 

检测一个 external(extern)常量或一个通知的名字应当比较它的地址(address)--而不是符号的名称, 判断是否为NULL or nil

 

三、弱连接整个Framework

 

比如一个在高版本中才出现的Framework,想在低版本使用他的特性。那你就必须弱连接那个使用的Framework,详见官方的图解---(其实就是在添加进去的Framework的 required 改成 optional)

 

http://developer.apple.com/library/ios/#documentation/DeveloperTools/Conceptual/XcodeProjectManagement/

130-Files_in_Projects/project_files.html#//apple_ref/doc/uid/TP40002666-SW4 

四、条件编译for不同的SDK

 

如果你不止基于一个IOS SDK编译,你就可能需要为base 

sdk使用条件化,可以使用在Availability.h中的定义。这个.h文件存在于系统的文件夹/usr/include的文件夹下,例如想在

Mac OS X v10.5(而不是IOS)中使用函数 CGColorCreateGenericCMYK

 

使用预处理指令for条件编译

 

#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED   

    // code only compiled when targeting Mac OS X and not iOS   

    // note use of 1050 instead of __MAC_10_5   

#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050   

    if (CGColorCreateGenericCMYK != NULL) {   

        CGColorCreateGenericCMYK(0.1,0.5.0.0,1.0,0.1);   

    } else {   

#endif   

    // code to create a color object with earlier technology   

#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050   

    }   

#endif   

#endif   

五、寻找出在程序中使用的以过时的实例

 

在IOS或Mac 

OS中有时候API会过时,但是过时不代表着那些就从Library或framework中删除,但是在使用的过程中会报出warning,并且在不远的

将来可能会被Apple从中移除。例如我们在code中使用了过时的函数 HPurge那么就会报出如下

 

'HPurge' is deprecated (declared at /Users/steve/MyProject/main.c:51) 

所以我们应当在工程中查找出如下的警告并且修改。

 

六、确定操作系统和Framework的版本

 

在运行时检查IOS的版本

 

NSString *osVersion = [[UIDevice currentDevice] systemVersion]; 

在运行时检查Mac OS X用Gestalt function 和 系统版本常量

 

另外,对于许多的Framework你可以在运行时检查指定Framework的版本。

 

例如:Application Kit(NSApplication.h)定义了NSAppKitVersionNumber常量---可以用来检查Application Kit Framework的版本

 

 

APPKIT_EXTERN double NSAppKitVersionNumber;   

#define NSAppKitVersionNumber10_0 577   

#define NSAppKitVersionNumber10_1 620   

#define NSAppKitVersionNumber10_2 663   

#define NSAppKitVersionNumber10_2_3 663.6   

#define NSAppKitVersionNumber10_3 743   

#define NSAppKitVersionNumber10_3_2 743.14   

#define NSAppKitVersionNumber10_3_3 743.2   

#define NSAppKitVersionNumber10_3_5 743.24   

#define NSAppKitVersionNumber10_3_7 743.33   

#define NSAppKitVersionNumber10_3_9 743.36   

#define NSAppKitVersionNumber10_4 824   

#define NSAppKitVersionNumber10_4_1 824.1   

#define NSAppKitVersionNumber10_4_3 824.23   

#define NSAppKitVersionNumber10_4_4 824.33   

#define NSAppKitVersionNumber10_4_7 824.41   

#define NSAppKitVersionNumber10_5 949   

#define NSAppKitVersionNumber10_5_2 949.27   

#define NSAppKitVersionNumber10_5_3 949.33 

所以我们可以像如下使用:

 

if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_0) {   

  /* On a 10.0.x or earlier system */   

} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_1) {   

  /* On a 10.1 - 10.1.x system */   

} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_2) {   

  /* On a 10.2 - 10.2.x system */   

} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_3) {   

  /* On 10.3 - 10.3.x system */   

} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_4) {   

  /* On a 10.4 - 10.4.x system */   

} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_5) {   

  /* On a 10.5 - 10.5.x system */   

} else {   

  /* 10.6 or later system */   

跟以上一样在 NSObjCRuntime.h中用定义了NSFoundationVersionNumber全局常量

 

小结:详解IOS SDK兼容性引导的内容介绍玩玩了,希望通过本文的学习能对你有所帮助!

 

原文地址:http://blog.csdn.net/diyagoanyhacker/article/details/6673344

 

26、NSDate 与 NSString 转换

 

将字符串 “Fri Nov 11 09:06:27 +0800 2011” 转换成Date:

 

    NSDateFormatter *format = [[NSDateFormatter alloc] init];

    NSLocale *enLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en-US"];

    [format setLocale:enLocale];

    [enLocale release];

    [format setDateFormat:@"EEE MMM dd HH:mm:ss ZZZ yyyy"];

    NSDate *dateTime = [format dateFromString:message];

 

 

 

将Date转换成字符串:

 

    NSDate *date = [NSDate date];

    NSString * dateString = [format stringFromDate:date];

 

//字符串转换成NSDate 需要设置NSLocale 否则真机上会失败。

 

27、数组中存储数据查询

 

NSMutableDictionary *userDic1 = [NSMutableDictionary dictionaryWithCapacity:10];

    NSMutableDictionary *userDic2 = [NSMutableDictionary dictionaryWithCapacity:10];

    [userDic1 setValue:@"Li" forKey:@"name"];

    [userDic2 setValue:@"Wang" forKey:@"name"];

    

    NSArray *userArray = [NSArray arrayWithObjects:userDic1,userDic2,nil];

    NSPredicate *namePredicate = [NSPredicate predicateWithFormat:@"SELF.name contains[cd] %@ ",@"L"];

    

    NSMutableArray *searchArray = [NSMutableArray arrayWithArray:[userArray filteredArrayUsingPredicate:namePredicate]];

    

    NSLog(@"searchArray  == %@",searchArray);

 

28、CoreText 总结

 

(1) NSAttributedString

 

      NSAttributedString 可以将一段文字中的部分文字设置单独的字体和颜色。

 

      与UITouch结合可以实现点击不同文字触发不同事件的交互功能。

 

       主要方法:

 

           - (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range;

 

           可以设置某段文字的字体名称,颜色,下滑线等信息。

 

           - (void)removeAttribute:(NSString *)name range:(NSRange)range;

 

           移除之前设置的字体属性值。

 

           - (void)addAttributes:(NSDictionary *)attrs range:(NSRange)range;

 

           存储某段文字包含的信息(包括字体属性或其它,也可以存储一些自定义的信息) 

 

           - (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range;

 

           通过location来获取某段文字中之前存储的信息NSDictionary

 

 

 

   //设置字体

   CTFontRef aFont = CTFontCreateWithName((CFStringRef)textFont.fontName, textFont.pointSize, NULL);

   if (!aFont) return;

   CTFontRef newFont = CTFontCreateCopyWithSymbolicTraits(aFont, 0.0, NULL, kCTFontItalicTrait, kCTFontBoldTrait);    //将默认黑体字设置为其它字体 

   [self removeAttribute:(NSString*)kCTFontAttributeName range:textRange];

   [self addAttribute:(NSString*)kCTFontAttributeName value:(id)newFont range:textRange];

   CFRelease(aFont);

   CFRelease(newFont);

   

   //设置字体颜色

   [self removeAttribute:(NSString*)kCTForegroundColorAttributeName range:textRange];

   [self addAttribute:(NSString*)kCTForegroundColorAttributeName value:(id)textColor.CGColor range:textRange];

   

   //设置对齐 换行

   CTTextAlignment coreTextAlign = kCTLeftTextAlignment;

   CTLineBreakMode coreTextLBMode = kCTLineBreakByCharWrapping;

   CTParagraphStyleSetting paraStyles[2] =

   {

       {.spec = kCTParagraphStyleSpecifierAlignment, .valueSize = sizeof(CTTextAlignment), .value = (const void*)&coreTextAlign},

       {.spec = kCTParagraphStyleSpecifierLineBreakMode, .valueSize = sizeof(CTLineBreakMode), .value = (const void*)&coreTextLBMode},

   };

   CTParagraphStyleRef aStyle = CTParagraphStyleCreate(paraStyles, 2);

   [self removeAttribute:(NSString*)kCTParagraphStyleAttributeName range:textRange];

   [self addAttribute:(NSString*)kCTParagraphStyleAttributeName value:(id)aStyle range:textRange];

   CFRelease(aStyle);

 

 

 

(2)Draw NSAttributedString

 

 

       

   CGContextRef cgc = UIGraphicsGetCurrentContext();

   CGContextSaveGState(cgc);

   

   //图像方向转换

   CGContextConcatCTM(cgc, CGAffineTransformScale(CGAffineTransformMakeTranslation(0, self.bounds.size.height), 1.f, -1.f));

   

   CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)weiBoText);

   drawingRect = self.bounds;

   CGMutablePathRef path = CGPathCreateMutable();

   CGPathAddRect(path, NULL, drawingRect);

   textFrame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0), path, NULL);

   CGPathRelease(path);

   CFRelease(framesetter);

   

   CTFrameDraw(textFrame, cgc);

   CGContextRestoreGState(cgc);

 

(3)图文混排

 

     CTFrameRef  textFrame     // coreText 的 frame

 

     CTLineRef      line             //  coreText 的 line

 

     CTRunRef      run             //  line  中的部分文字

 

     相关方法:

 

   

   CFArrayRef CTFrameGetLines    (CTFrameRef frame )      //获取包含CTLineRef的数组

 

   void CTFrameGetLineOrigins(

   CTFrameRef frame,

   CFRange range,

   CGPoint origins[] )  //获取所有CTLineRef的原点

 

 CFRange CTLineGetStringRange  (CTLineRef line )    //获取line中文字在整段文字中的Range

 

 CFArrayRef CTLineGetGlyphRuns  (CTLineRef line )    //获取line中包含所有run的数组

 

 CFRange CTRunGetStringRange  (CTRunRef run )     //获取run在整段文字中的Range

 

 CFIndex CTLineGetStringIndexForPosition(

   CTLineRef line,

   CGPoint position )   //获取点击处position文字在整段文字中的index

 

   CGFloat CTLineGetOffsetForStringIndex(

   CTLineRef line,

   CFIndex charIndex,

   CGFloat* secondaryOffset ) //获取整段文字中charIndex位置的字符相对line的原点的x值

 

  主要步骤:

 

       1)计算并存储文字中保含的所有表情文字及其Range

 

       2)替换表情文字为指定宽度的NSAttributedString

 

           CTRunDelegateCallbacks callbacks;

   callbacks.version = kCTRunDelegateVersion1;

   callbacks.getAscent = ascentCallback;

   callbacks.getDescent = descentCallback;

   callbacks.getWidth = widthCallback;

   callbacks.dealloc = deallocCallback;

   

   CTRunDelegateRef runDelegate = CTRunDelegateCreate(&callbacks, NULL);

   NSDictionary *attrDictionaryDelegate = [NSDictionary dictionaryWithObjectsAndKeys:

                                           (id)runDelegate, (NSString*)kCTRunDelegateAttributeName,

                                           [UIColor clearColor].CGColor,(NSString*)kCTForegroundColorAttributeName,

                                           nil];

   

   NSAttributedString *faceAttributedString = [[NSAttributedString alloc] initWithString:@"*" attributes:attrDictionaryDelegate];

   

   [weiBoText replaceCharactersInRange:faceRange withAttributedString:faceAttributedString];

   [faceAttributedString release];    

 

       3)  根据保存的表情文字的Range计算表情图片的Frame

 

                textFrame 通过CTFrameGetLines 获取所有line的数组 lineArray

 

               遍历lineArray中的line通过CTLineGetGlyphRuns获取line中包含run的数组 runArray

 

               遍历runArray中的run 通过CTRunGetStringRange获取run的Range

 

               判断表情文字的location是否在run的Range

 

               如果在 通过CTLineGetOffsetForStringIndex获取x的值 y的值为line原点的值

 

       4)Draw表情图片到计算获取到的Frame

 

 

 

(3)点击文字触发事件

 

  

 

  主要步骤:

 

       1) 根据touch事件获取点point

 

       2)   textFrame 通过CTFrameGetLineOrigins获取所有line的原点

 

       3) 比较point和line原点的y值获取点击处于哪个line

 

       4)  line、point 通过CTLineGetStringIndexForPosition获取到点击字符在整段文字中的    index   

 

       5)  NSAttributedString 通过index 用方法-(NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range  可以获取到点击到的NSAttributedString中存储的NSDictionary

 

       6) 通过NSDictionary中存储的信息判断点击的哪种文字类型分别处理

 

23、视频播放、编辑

 

 

 

 

 

 

转载: http://www1.huachu.com.cn/read/readbookinfo.asp?sectionid=1000006772

 

 

对于不同的设备,视频功能是各不相同的。所有的设备都能够回放视频,但是仅有iPhone 3GS设备有记录视频的能力。对于每个设备而言,都存在API能够让您检测哪些功能是可用的和哪些功能是不可用的,这样就能够针对使用视频功能的所有用户创建优秀的用户体验。这一节内容讨论如何才能将视频集成到应用程序中,以及如何才能使用特定的设备记录视频。

 

10.2.1  播放视频

 

iPhone SDK提供了一个简单的控制器来在应用程序中播放视频。MPMoviePlayerController类位于MonoTouch.MediaPlayer名称空间中,它提供了播放影片、准备播放影片和停止播放影片的功能。程序清单10-9说明了如何开始播放视频。第一行代码显示的是实例化一个新的影片播放器对象,并将视频内容文件路径传入其构造函数内。第二行代码简单地调用了Play方法,该方法显示影片播放器并开始播放视频。

 

程序清单10-9  播放视频文件

 

var player = new MPMoviePlayerController(NSUrl.FromFilename("video.mp4"));

 

player.Play();

 

如果希望使用外部视频,并以渐进方式下载视频,而不是直接播放设备上的视频,那么就要使用NSUrl的FromString方法,而不是使用FromFilename,并将视频的URL地址传入其中。当然,需要使用自己的外部视频替换这里的URL字符串。

 

var videoUrl = NSUrl.FromString("http://example.com/video.mp4")

 

var player = new MPMoviePlayerController(videoUrl);

 

player.Play();

 

您也许会注意到,在视频开始播放之前有一个短暂的缓冲周期。这就是所谓的预加载过程。可以设置视频播放器上的通知,在视频已经开始预加载时让其引发一条消息,这样就能够显示加载屏幕,然后在预加载完成后开始显示视频。当视频完成播放或者用户改变视频的缩放比例时,也可以使用通知引发消息。

 

使用NSNotificationCenter类的DefaultCenter属性添加观察者,从而在通知产生时引发这些消息。可以将观察者看作将事件添加到特定动作上的一种方式,使用字符串作为创建这些事件的方式,而不是使用您也许已经熟悉的C#内的强类型事件。对于本示例而言,只需要使用通知MPMoviePlayerContentPreloadDidFinishNotification和MPMoviePlayerPlaybackDid- FinishNotification。另一个通知名为MPMoviePlayerScalingModeDidChangeNotification。还要添加两个NSObject类型的类变量,它们可以作为预加载和回放完成的观察者。使用AddObserver方法,为每个观察者传入一个动作方法,当通知被调用时这些动作方法就会运行。可以使用Lambda表达式以内联方式放置这段代码。当预加载通知被激活以后,只需要开始播放视频,因此可以调用MPMoviePlayerController上的Play方法;当回放通知激活以后,需要移除观察者,并清除影片播放器实例。可以使用活动指示器确保让用户知道当应用程序启动时正在预加载视频。程序清单10-10给出了完成这项工作的示例代码。同样,需要确保改变第8行代码中的URL,让其指向选择的外部视频。

 

程序清单10-10  使用影片播放器观察者

 

MPMoviePlayerController moviePlayer;

 

NSObject didFinishPreload, didFinishPlayback;

 

 

public override void ViewDidLoad()

 

{

 

  base.ViewDidLoad ();

 

 

 

  var videoUrl = NSUrl.FromString("http://example.com/video.mp4");

 

  moviePlayer = new MPMoviePlayerController(videoUrl);

 

 

 

  activityIndicator.StartAnimating();

 

 

  var centre = NSNotificationCenter.DefaultCenter;

 

  var preloadFinish = "MPMoviePlayerContentPreloadDidFinishNotification";

 

  didFinishPreload = centre.AddObserver(preloadFinish,

 

     (notify) => {

 

        Console.WriteLine ("Start playing movie");

 

        activityIndicator.StopAnimating();

 

        moviePlayer.Play();

 

     });

 

 

  var playbackFinished = "MPMoviePlayerPlaybackDidFinishNotification";

 

  didFinishPlayback = centre.AddObserver(playbackFinished,

 

     (notify) => {

 

        Console.WriteLine ("Movie finished, Clean up");

 

 

        centre.RemoveObserver(didFinishPreload);

 

        centre.RemoveObserver(didFinishPlayback);

 

 

        activityIndicator.Hidden = true;

 

 

        moviePlayer.Dispose();

 

        moviePlayer = null;

 

     }); }

 

}

 

10.2.2  定制视频播放器

 

影片播放器提供的功能是相当有限的,仅允许对两个属性根据需要进行调整,这两个属性是ScalingMode和MovieControlMode。

 

缩放模式对播放影片的长宽比进行设置。可用的长宽比选项为Fill、AspectFill、AspectFit和None。

 

Fill选项用于让视频填满整个屏幕,这样视频的边缘会与屏幕吻合,但是可能无法保持原有的长宽比。

 

AspectFill选项在视频填满整个屏幕时不会扭曲视频,但是确实会对视频进行裁剪,这样视频才能够无缝地填满这个屏幕。

 

AspectFit选项会保留视频原有的长宽比,并让视频的边缘尽可能地与屏幕吻合,但如果视频不是完全吻合,那么可能会露出背景视图。

 

None选项不会调整视频,而是按照视频自身的大小进行播放。

 

使用MPMovieScalingMode枚举,可以将ScalingMode设置为这个列表中的任何选项。参见图10-10~图10-13,其中给出了每一种缩放模式的示例。播放器的背景设置为蓝色(在这些图中,背景是以浅灰色打印版本进行显示的),因此可以看到视频大小和播放器大小之间的差异。注意,在这个示例中,Aspect Fit和Fill视频是一样的。这是因为视频的当前长宽比允许视频的边缘与屏幕吻合,而无须改变长宽比。

 

 

图  10-10                                      图  10-11  

 

 

图  10-12                                    图  10-13  

 

另外一个可以针对视频播放器修改的属性是MovieControlMode。使用MPMovieControlMode枚举,可以将控件模式设置为Default、Hidden或VolumeOnly。图10-14和图10-15给出了Default和VolumeOnly控件模式。Hidden模式隐藏了屏幕上所有默认的动作;如果希望让自己提供的用户界面位于视频的上方,那么该模式就很有用。

 

 

图  10-14                                     图  10-15  

 

影片播放器本身是作为一个关键窗口出现在屏幕上的。为了将自定义界面添加到影片播放器关键窗口的上方,可以从应用程序中具有的窗口列表内获取该关键窗口的引用(关键窗口在本质上是窗口框架内可以获得的最顶层的可见视图)。然后可以简单地将子视图添加到影片播放器关键窗口中。因为影片播放器是以横屏模式出现的,所以必须对叠加视图进行变换,从而使其能够与横屏模式匹配。程序清单10-11说明了如何使用代码实现此处提到的所有内容。

 

程序清单10-11  在视频播放器上叠加视图

 

public MPMoviePlayerController mPlayer;

 

 

public override void ViewDidLoad ()

 

{

 

  base.ViewDidLoad ();

 

 

  var button = UIButton.FromType(UIButtonType.RoundedRect);

 

  button.Frame = new RectangleF(0f, 20f, 320f, 40f);

 

  button.SetTitle("Play Video", UIControlState.Normal);

 

  button.TouchUpInside += delegate(object sender, EventArgs e)

 

  {

 

     PlayMovie ();

 

 

     var windows = UIApplication.SharedApplication.Windows;

 

     if(windows.Count() > 1)

 

     {

 

        var moviePlayerWindow = UIApplication.SharedApplication.KeyWindow;

 

        var customView = new MyOverlayView(this);

 

        moviePlayerWindow.AddSubview(customView);

 

     }

 

  };

 

  this.View.AddSubview(button);

 

}

 

 

void PlayMovie ()

 

{

 

  var url = NSUrl.FromFilename("video.mp4");

 

  mPlayer = new MPMoviePlayerController(url);

 

  mPlayer.Play();

 

}

 

 

...

 

 

public class MyOverlayView : UIView

 

{

 

  public MyOverlayView (MainViewController mvc)

 

  {

 

     this.Frame = new RectangleF(0f, 0f, 320f, 480f);

 

     this.Transform = CGAffineTransform.MakeRotation((float)(Math.PI / 2));

 

 

     UIButton button = UIButton.FromType(UIButtonType.RoundedRect);

 

     button.SetTitle("Pause", UIControlState.Normal);

 

     button.Frame = new RectangleF(65f, 360f, 190f, 32f);

 

     button.TouchUpInside += delegate(object sender, EventArgs e) {

 

        Console.WriteLine ("Paused the video");

 

        mvc.mPlayer.Pause();

 

     };

 

 

     this.AddSubview(button);

 

  }

 

}

 

图10-16显示的是在程序清单10-11中创建的叠加视图。

 

 

图  10-16  

 

10.2.3  选取视频

 

为了让用户可以从存储在设备上的视频列表中选取视频,可以使用UIImagePickerController,在本章前面已经使用过该类。因为面向iPhone的视频功能非常类似于摄像头功能,所以看到视频功能属于UIImagePickerController类的组成部分并不令人感到惊奇。在程序清单10-6中,我们使用IsSourceTypeAvailable方法来确定设备是否具有摄像头。因为视频功能仅限于iPhone3GS模型,所以只是弄清楚是否存在摄像头并不足够。这时就需要使用UIImagePickerController类上的AvailableMediaTypes静态方法。

 

AvailableMediaTypes方法以源类型作为输入,返回设备上可用媒体类型的数组。图像的媒体类型为public.image,而视频的媒体类型为public.movie。如果该方法返回public.movie类型,那么可以将UIImagePickerController实例的MediaTypes属性设置为仅有public.movie媒体类型。程序清单10-12给出了以这种方式设置的选取器。

 

程序清单10-12  使用图像选取器控制器选取视频

 

if (HasVideoSupport())

 

{

 

  UIImagePickerController picker = new UIImagePickerController();

 

  picker.SourceType = UIImagePickerControllerSourceType.PhotoLibrary;

 

  picker.MediaTypes = new []{"public.movie"};

 

  picker.Delegate = new MyImagePickerDelegate(this);

 

  this.PresentModalViewController(picker, true);

 

}

 

else

 

{

 

  using (var alert = new UIAlertView("Whoops", "No video support found",

 

                                      null, "Ok!", null))

 

  {

 

     alert.Show();

 

  }

 

}

 

 

...

 

 

bool HasVideoSupport()

 

{

 

  var cameraType = UIImagePickerControllerSourceType.Camera;

 

  var cameraSupport =

 

                 UIImagePickerController.IsSourceTypeAvailable(cameraType);

 

  return (!cameraSupport) ? false :

 

         UIImagePickerController.AvailableMediaTypes(cameraType)

 

                                       .Contains("public.movie");

 

}

 

在显示该选取器时,注意到您只看到了视频,因为视频是可以选取的唯一的媒体类型。图10-17说明了如何显示只选取视频的选取器。

 

 

图  10-17  

 

在选取视频以后,该选取器使用与图像选取器相同的回调:FinishedPickingMedia。NSDictionary带有两个键:UIImagePickerControllerMediaURL和UIImagePickerControllerMediaType。媒体URL包含一个指向所选择视频的临时位置的NSUrl对象。这就是您可以处理视频的地方—— 要么将视频移动到Documents文件夹中,要么通过其他方式使用视频。临时视频存储在应用程序的tmp文件夹中,OS在认为合适的时候会将其清除。

 

10.2.4  记录视频

 

使用iPhone记录视频非常类似于使用iPhone进行拍照。在前一个示例中,可以仅将源类型设置为摄像头而不是图片库的默认源类型,所有这些设置都是为了记录视频而不是拍摄照片。当然,这就是假设您已经使用AvailableMediaTypes方法验证了该设备可以记录视频。注意,摄像头控件会发生变化,从而反映出是记录视频而不是拍摄照片。作为可选项,可以选择通过将ShowsCameraControls设置为false来隐藏摄像头控件;然而,在编写本书的时候,如果不使用默认的摄像头控件,那么还没有方式可以通过编程来记录视频。程序清单10-13是设置UIImagePickerController以记录视频的一个示例。图10-18显示的是视频记录屏幕。

 

程序清单10-13  记录视频

 

public override void ViewDidLoad ()

 

{

 

  base.ViewDidLoad ();

 

 

  UIButton button = UIButton.FromType(UIButtonType.RoundedRect);

 

  button.Frame = new RectangleF(0f, 30f, 320, 40f);

 

  button.SetTitle("Record Video", UIControlState.Normal);

 

  button.TouchUpInside += delegate(object sender, EventArgs e) {

 

 

     var cameraType = UIImagePickerControllerSourceType.Camera;

 

     if(HasVideoSupport())

 

     {

 

        UIImagePickerController picker = new UIImagePickerController();

 

        picker.SourceType = cameraType;

 

        picker.MediaTypes = new []{"public.movie"};

 

        picker.Delegate = new MyImagePickerDelegate(this);

 

        this.PresentModalViewController(picker, true);

 

     }

 

     else

 

     {

 

        using (var alert = new UIAlertView("Whoops",

 

                          "No video support found", null, "Ok!", null))

 

        {

 

           alert.Show();

 

        }

 

     }

 

  };

 

  this.View.AddSubview(button);

 

}

 

 

...

 

 

bool HasVideoSupport()

 

{

 

  var cameraType = UIImagePickerControllerSourceType.Camera;

 

  var cameraSupport =

 

                 UIImagePickerController.IsSourceTypeAvailable(cameraType);

 

  return (!cameraSupport) ? false :

 

         UIImagePickerController.AvailableMediaTypes(cameraType)

 

                                       .Contains("public.movie");

 

}

 

 

图  10-18  

 

在记录视频时,还可以设置视频记录的质量。质量越低,那么得到的视频文件越小。可以像下面的代码这样使用UIImagePickerControllerQualityType枚举设置图像选取器的VideoQuality属性:

 

picker.VideoQuality =  UIImagePickerControllerQualityType.Low;

 

该枚举提供3个质量选项:High、Medium和Low。视频记录中使用的默认设置是Medium。在记录视频时可以使用的另外一个属性是VideoMaximumDuration,这个属性用于设置记录视频的最长持续时间(单位为秒)。视频最长可以是10分钟,记录视频的默认时间值也是10分钟。

 

10.2.5  编辑视频

 

采用与通过UIImagePickerController编辑图像相同的方式,在记录或选取视频时也可以将AllowEditing属性设置为true。这样,从图像选取器中选取了视频以后,就能够有一个接口对视频进行裁剪。与编辑图像时不同,在完成编辑时,只会使用NSDictionary中的UIImagePickerCon-trollerMediaURL键获得临时裁剪或编辑的视频(而不是原始视频),该键在选择或记录视频后可用。临时创建的视频最终会被设备自动地清除。UIImagePickerControllerMediaURL对象属于NSUrl类型,需要将该视频对象强制转换为NSUrl类型来提取定位该文件所需的Path属性。图10-19显示了图像选取器视频编辑器的外观。

 

 

图  10-19

 

但是,视频选取器不是编辑视频最值得推荐的方式。对于编辑视频而言,最好使用专门的UIVideoEditorController类作为替代。该视频编辑器控制器为您提供了一个编辑窗口,它类似于图像选取器所使用的编辑窗口。然而,注意到用户有两个选项可以选择:Cancel和Save。UIVideoEditorController类对此提供了3个不同的事件:

 

UserCancelled  当用户单击Cancel按钮时处理这个事件。

 

Saved  当用户单击Save按钮时处理该事件。

 

Failed  当产生无法预料的错误时会引发该事件,例如视频的格式无法编辑这样的错误。

 

Saved事件返回编辑好的视频文件的路径,Failed事件返回一个NSError对象,UserCancelled事件不会返回任何额外的信息。如果希望获得原始文件的路径,那么可以将发送方强制转换为UIVideoEditorController对象,然后使用该对象的VideoPath属性。

 

 

视频编辑器只能用在直屏模式中

 

 

 

 

有了视频编辑器以后,通过分别设置VideoQuality和VideoMaximumDuration属性,就可以将编辑好的视频设置为较低的质量和强制编辑好的视频具有最大时间长度。

 

创建视频编辑界面是相当直观的。实例化一个新的UIVideoEditorController,将VideoPath设置为希望编辑的视频的路径,然后将视频编辑器作为一个模态视图展示。因为无法知道运行该应用程序的iPhone是否支持对视频进行编辑,所以需要使用视频编辑器的静态方法CanEditVideoAtPath。传入视频路径,如果该视频可以编辑,那么该方法就返回true。程序清单10-14给出了一个创建专用视频编辑器界面的示例,图10-20给出了UIVideoEditorController界面的显示外观。

 

程序清单10-14  使用专用视频编辑器界面

 

if(UIVideoEditorController.CanEditVideoAtPath(ChosenVideoPath))

 

{

 

  var videoEditor = new UIVideoEditorController();

 

  videoEditor.VideoPath = ChosenVideoPath;

 

  videoEditor.Saved += delegate(object sender, UIPathEventArgs e) {

 

     this.DismissModalViewControllerAnimated(true);

 

     // Handle edited video with e.Path

 

  };

 

  videoEditor.Failed += delegate(object sender, NSErrorEventArgs e) {

 

     this.DismissModalViewControllerAnimated(true);

 

     // Handle error here with e.Error

 

  };

 

  videoEditor.UserCancelled += delegate(object sender, EventArgs e) {

 

     this.DismissModalViewControllerAnimated(true);

 

     // Handle cancel

 

  };

 

  this.PresentModalViewController(videoEditor, true);

 

}

 

 

图10-20

 

10.2.6  将视频保存到相册

 

在将图像保存到相册时,要使用UIImage类上的静态方法保存文件。因为对视频文件的所有引用都使用路径而不是内存中的对象,所以UIVideo静态类提供了将视频保存到相册中所需的方法。视频功能仅限于特定的设备,因此在将视频保存到相册之前,需要检查一下该设备是否能够真正将视频保存到其相册中。静态方法IsCompatibleWithSavedPhotosAlbum提供了这种功能,如果传入可以保存到相册的视频的路径,那么该方法就会返回true。

 

为了将视频保存到相册中,一旦通过检查确定该设备确实可以保存视频,那么就可以使用UIVideo类上的静态方法SaveToPhotosAlbum。将希望保存的视频的路径传入该方法,当保存视频以后会触发一个回调函数。程序清单10-15给出了完成这些任务的代码。

 

程序清单10-15  将视频保存到相册

 

var videoPath = videoSavePath;

 

if(UIVideo.IsCompatibleWithSavedPhotosAlbum(videoPath))

 

{

 

  UIVideo.SaveToPhotosAlbum(videoPath, delegate (string path,

 

                                                 NSError errors)

 

  {

 

     using (var alert = new UIAlertView("Success", "Video saved!",

 

                                        null, "Ok!", null))

 

     {

 

        alert.Show();

 

     }

 

  });

 

}

 

 

24、CoreText基础-字体必修课

 

 

转自:http://www.dreamingwish.com/dream-2011/coretext-ji-chu-font-basis.html

 

介绍一些字体的术语,以及对应的英文名称

 

字体(Font):是一系列字号、样式和磅值相同的字符(例如:10磅黑体Palatino)。现多被视为字样的同义词

 

字面(Face):是所有字号的磅值和格式的综合

 

字体集(Font family):是一组相关字体(例如:Franklin family包括Franklin Gothic、Fran-klinHeavy和Franklin Compressed)

 

磅值(Weight):用于描述字体粗度。典型的磅值,从最粗到最细,有极细、细、book、中等、半粗、粗、较粗、极粗

 

样式(Style):字形有三种形式:Roman type是直体;oblique type是斜体;utakuc type是斜体兼曲线(比Roman type更像书法体)。

 

x高度(X height):指小写字母的平均高度(以x为基准)。磅值相同的两字母,x高度越大的字母看起来比x高度小的字母要大

 

Cap高度(Cap height):与x高度相似。指大写字母的平均高度(以C为基准)

 

下行字母(Descender):例如在字母q中,基线以下的字母部分叫下伸部分

 

上行字母(Ascender):x高度以上的部分(比如字母b)叫做上伸部分

 

基线(Baseline):通常在x、v、b、m下的那条线

 

描边(Stroke):组成字符的线或曲线。可以加粗或改变字符形状

 

衬线(Serif):用来使字符更可视的一条水平线。如字母左上角和下部的水平线。

 

无衬线(Sans Serif):可以让排字员不使用衬线装饰。

 

方形字(Block):这种字体的笔画使字符看起来比无衬线字更显眼,但还不到常见的衬线字的程度。例如Lubalin Graph就是方形字,这种字看起来好像是木头块刻的一样

 

手写体脚本(Calligraphic script):是一种仿效手写体的字体。例如Murray Hill或者Fraktur字体

 

艺术字(Decorative):像绘画般的字体

 

Pi符号(Pisymbol):非标准的字母数字字符的特殊符号。例如Wingdings和Mathematical Pi

 

连写(Ligature):是一系列连写字母如fi、fl、ffi或ffl。由于字些字母形状的原因经常被连写,故排字员已习惯将它们连写。

 

 

 

25、准确计算CoreText高度的方法

 

- (int)getAttributedStringHeightWithString:(NSAttributedString *)  string  WidthValue:(int) width

{

   int total_height = 0;

   

   CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string);    //string 为要计算高度的NSAttributedString

   CGRect drawingRect = CGRectMake(0, 0, width, 1000);  //这里的高要设置足够大

   CGMutablePathRef path = CGPathCreateMutable();

   CGPathAddRect(path, NULL, drawingRect);

   CTFrameRef textFrame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0), path, NULL);

   CGPathRelease(path);

   CFRelease(framesetter);

   

   NSArray *linesArray = (NSArray *) CTFrameGetLines(textFrame);

   

   CGPoint origins[[linesArray count]];

   CTFrameGetLineOrigins(textFrame, CFRangeMake(0, 0), origins);

   

   int line_y = (int) origins[[linesArray count] -1].y;  //最后一行line的原点y坐标

   

   CGFloat ascent;

   CGFloat descent;

   CGFloat leading;

   

   CTLineRef line = (CTLineRef) [linesArray objectAtIndex:[linesArray count]-1];

   CTLineGetTypographicBounds(line, &ascent, &descent, &leading);

   

   total_height = 1000 - line_y + (int) descent +1;    //+1为了纠正descent转换成int小数点后舍去的值

   

   CFRelease(textFrame);

   

   return total_height;

   

}

 

 

 

//关于line坐标位置y为下图黑线所在位置 descent为黑线下部分字体的高度

 

//关于字体各部分高度说明  http://ios-iphone.diandian.com/post/2012-03-29/18055023  

 

 

26、自定义拷贝、粘贴窗口

 

(1)、重写canBecomeFirstResponder方法

 

  - (BOOL)canBecomeFirstResponder{

  

  [super canBecomeFirstResponder];

  return YES;

}

 

(2)、创建自定义UIMenuController

 

  UIMenuItem *share = [[UIMenuItem alloc] initWithTitle:@"分享" action:@selector(share:)];

   UIMenuItem *email = [[UIMenuItem alloc] initWithTitle:@"邮件" action:@selector(email:)];

   UIMenuItem *print = [[UIMenuItem alloc] initWithTitle:@"打印" action:@selector(print:)];

 

   UIMenuController *menu = [UIMenuController sharedMenuController];

   [menu setMenuItems:[NSArray arrayWithObjects:share, email,print, nil]];

   [menu setTargetRect:self.frame inView:self.superview];

   [menu setMenuVisible:YES animated:YES];

 

(3)、判断显示哪个menu

 

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender

{

  [super canPerformAction:action withSender:sender];

 

  if ( action == @selector(share:) || action == @selector(email:) || action == @selector(print:))

  {

      return YES;    

  }

  else

  {

      return NO;

  }

}

 

 

 

 

27、iOS本地推送通知方法

 

  

可在应用后台执行时,本地弹出推送通知,也可以定时触发推送。

 

- (void)applicationDidEnterBackground:(UIApplication *)application

{

   

   UIDevice* device = [UIDevice currentDevice];

   

   BOOL backgroundSupported = NO;

   

   if ([device respondsToSelector:@selector(isMultitaskingSupported)])

   {    

       backgroundSupported = device.multitaskingSupported;

   }

   if (backgroundSupported && _bgTask==UIBackgroundTaskInvalid)

   {

       UIApplication *app = [UIApplication sharedApplication];

       

       _bgTask = [app beginBackgroundTaskWithExpirationHandler:^{

       }];  

       

       

       dispatch_async(dispatch_get_main_queue(), ^{

           

           while (app.applicationState==UIApplicationStateBackground && _bgTask!=UIBackgroundTaskInvalid  && [app backgroundTimeRemaining] > 10)  

           {

               [NSThread sleepForTimeInterval:1];

               NSLog(@"background task %d left left  time %d.", _bgTask, (int)[app backgroundTimeRemaining]);

                               

               if ([app backgroundTimeRemaining] < 580)

               {

                   UILocalNotification *localNotif = [[UILocalNotification alloc] init];

                   if (localNotif)

                   {

                       localNotif.alertBody = [NSString stringWithString:@"测试本地通知消息,后台提示功能。"];

                       localNotif.alertAction = NSLocalizedString(@"查看", nil);

                       localNotif.soundName = UILocalNotificationDefaultSoundName;

                       localNotif.applicationIconBadgeNumber = 1;            

                       [application presentLocalNotificationNow:localNotif];

                       [localNotif release];

                       break;

                   }

               }

           }

           

           NSLog(@"background task %d finished.", _bgTask);     

           [app endBackgroundTask:_bgTask];

           _bgTask = UIBackgroundTaskInvalid;   

              

       });      

   }

 

}

 

28、CoreText绘制文本出现行间距不等及解决办法

 

 

转自: http://willonboy.tk/?p=1163

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

最终在http://www.cocoanetics.com/2012/02/radar-coretext-line-spacing-bug/

 

找到了DTCoreText库

 

 

Radar: “CoreText Line Spacing Bug” b 05, 2012

 

I finally got around to report an annoying bug in CoreText that has been bugging us in DTCoreText until I wrote a method to correct line origins as a workaround. rdar://10810114

 

The annoying thing about this bug is that it adds visual noise to otherwise pristinely rendered text. Especially on larger font sizes you see that additional space appears before each CTLine that ends with a paragraph break (\n).

 

UPDATE: This is a duplicate of rdar://9931615.

 

 

 

CoreText Line Spacing BugSummary

 

CoreText inserts too much space before any line that ends with a \n. This extra space depends on the font and font size. On large print this causes visual noise by not being uniform.

 

Steps to Reproduce

 

Create a CTFrame from a CTFrameSetter with a string that is long enough to wrap and that contains paragraph breaks. Use a non-UI font, like for example AriaMT.

 

Expected Results

 

Line origins should be spaced by exactly the same distance for identical text and identical attributes.

 

Actual Results

 

Each line that ends with a paragraph break is shifted down. With the system UI font, size 54 baselines are spaced exactly 64 pixels apart. With ArialMT, size 54, baseline spacing differs between 62 and 65.

 

Regression

 

This has been a bug since before iOS 4.3.

 

Notes

 

This does not occur with all fonts, Using a system font the spacing is precisely correct. I have attached a project to demonstrate the issue. See TextView.m.

 

It appears that the text metrics for an (invisible) paragraph glyph are miscalculated. Since the glyph is not visible you’d expect neither and ascender nor descender value. But instead the descender is too large. If you walk through the entire line and get the maximum ascenders and descenders the value is correct if you omit the \n in this calculation.

 

In short: A trailing \n messes up the font metrics for the entire CTLine.

 

Attachment: CoreTextLineOrigins Demo Project

分享到:
评论

相关推荐

    黑马iOS知识点全总结

    黑马iOS往期学员,总结面试所需知识点,基本全包括!C,OC,Foundation!

    iOS基础知识大总结

    iOS开发基础涵盖了许多关键知识点,包括面向对象编程的三大特性:多肽(多态)、继承和封装,以及网络通信、数据解析和用户界面设计。在iOS应用开发中,这些概念和技术构成了开发流程的基础。 首先,多态(多肽)...

    iOS 10 适配知识点总结 - 简书1

    iOS 10 适配知识点总结 iOS 10 适配知识点总结是指在 iOS 10 系统中,开发者需要注意的一些关键点,以确保 App 的正常运行和适配。下面是 iOS 10 适配知识点总结: 1. Xcode 8.0 的使用 在 iOS 10 中,Xcode 8.0...

    iOS项目开发知识点总结

    ### iOS项目开发知识点总结 #### 一、键盘事件处理 在iOS应用开发中,经常会遇到对键盘事件的处理。例如,当用户在一个文本框中完成输入后,希望可以通过按下键盘上的“返回”键来关闭键盘。 ```objective-c - ...

    黑马iOS入学面试知识点全总结

    【iOS基础知识点总结】 在iOS开发领域,掌握扎实的基础知识是至关重要的,这不仅关乎面试的成功,也是成为一名优秀iOS开发者的基础。"黑马iOS入学面试知识点全总结"包含了C语言、Objective-C(OC)以及Foundation...

    swift-iOS常用的知识点总结

    本总结主要针对iOS开发中的Swift常用知识点进行详细阐述,帮助初学者快速掌握Swift语言并构建UI。 1. **变量与常量** Swift中的`let`用于声明常量,一旦赋值后不可更改;`var`用于声明变量,其值可以改变。例如: ...

    ios开发技巧和知识点总结

    有很多的ios开发中会遇到的知识点和开发技巧,对于开发人员 绝对有帮助

    iOS常见算法以及应用知识点总结

    本文将对iOS开发中的一些基础算法进行总结,包括冒泡排序和快速排序。 首先,我们来看冒泡排序。冒泡排序是一种简单直观的排序算法,它重复地遍历待排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换...

    2018秋招iOS面试总结

    以下是对2018年秋招iOS面试中常见知识点的详尽解析: 1. **Objective-C与Swift基础**: - Objective-C:面试中常问的基础知识包括类、对象、继承、消息传递、协议等。Objective-C的动态性,如Category和KVC(Key-...

    iOS教学各类知识点总结运行时贝塞尔曲线水纹粒子发射器核心动画渐变色网络请求按钮_BigShow1949.zip

    iOS教学各类知识点总结运行时贝塞尔曲线水纹粒子发射器核心动画渐变色网络请求按钮_BigShow1949

    活力小清新iOS风格工作总结报告PPT模板.pptx

    【知识点详述】: 1. **PPT模板设计**:PPT模板是预先设计好的演示文稿格式,包含了页面布局、颜色搭配、字体选择等元素,便于用户快速构建专业且统一的演示文稿。这个模板特别之处在于采用了“活力小清新”的设计...

    IOS开发&&swift知识点梳理及总结

    swift知识点梳理,个人笔记。包含常量与变量、基本数据类型,以及Swift语言中的新数据类型—元组型和可选型的内容。同时还讲解了运算符和表达式、流程控制语句、字符和字符串、集合类型、函数和闭包。此外,Swift...

    多年iOS开发经验总结

    总结起来,多年iOS开发经验总结中提及的这些知识点涵盖了在iOS开发过程中常见的一些问题和解决方法。通过使用UIKit和Foundation框架提供的API,开发者能够解决界面设计、多线程操作、数据类型比较、日期时间计算等...

    梦幻朦胧背景半透明点线创意iOS风工作总结报告ppt模板.zip

    模板的特点集中在以下几个关键知识点上: 1. **iOS风格**:iOS风格通常指的是简洁、清晰且具有现代感的设计,常用于科技、创新相关的演示文稿。这种风格强调平滑的过渡效果、清晰的图标和简洁的布局,旨在提供一种...

    iOS面试题总结

    虽然文件内容仅适合初学者,但它涉及了iOS开发中的多个重要知识点。以下是对文档中提及知识点的详细解读: 1. Objective-C的内存管理 在Objective-C中,内存管理是开发者必须掌握的核心技能之一。文件中提到了与...

    IOS小知识点

    ### iOS小知识点总结 #### 一、Objective-C基础特性 **动态类型(Dynamic Typing)**:Objective-C 支持动态类型,这意味着可以在运行时确定对象的实际类型。 **动态绑定(Dynamic Binding)**:方法调用是在运行时...

    iOS block使用总结

    在iOS开发中,Block是一种强大的、灵活的...通过上述知识点,我们可以更深入地理解Block的工作原理,并有效地利用它来编写更加简洁高效的代码。在实际项目中,尤其是涉及到异步操作和事件处理时,Block的运用尤其关键。

    CISCO路由器IOS升级方法总结

    在进行IOS升级时,需要注意以下几点: 1. 防止路由器断电,导致升级失败。 2. 确保计算机与路由器的连接正确,防止连接不当导致升级失败。 3. 在升级前备份原有IOS文件,防止待升级的IOS文件存在问题不可用。 五、...

    ios学习总结

    在iOS开发领域,这是一份个人学习经验的总结,涵盖了从入门到进阶的关键知识点。iOS开发主要使用Swift编程语言,这是一种由Apple公司推出,旨在提升开发效率,同时保持代码简洁和安全的语言。Swift语法清晰,易于...

    iOS开发之一些实用小知识点总结

    在iOS开发过程中,掌握一些实用的小知识点能够极大地提高开发效率并优化用户体验。本文将探讨防止UIButton和cell重复点击、获取当前视图最顶层的ViewController以及如何通过代码进行屏幕截图这三大核心知识点。 ...

Global site tag (gtag.js) - Google Analytics