iOS开发——OC篇&常用问题解答(二)

101、编译错误:ld: library notfound for -lPods

当项目中使用了 cocoaPods 时,经常出现此错误(通常是 release 的时候)。

这是由于 pod install 后,cocoaPods 会创建一个新的 workspace。你必须关闭项目并重新打开。问题即可解决。

102、为什么 iOS 的时间总是比真实时间慢8小时       

例 如,一个北京时间"2014-4-4 22:00"(字符串),需要转换成 NSDate。字符串转换成 NSDate 一般是通过 NSDateFormatter 进行的。而在 iOS 上 NSDate 以 GMT 时间存储,因此NSDateFormatter会自动将字符串当前时区的本地时间处理,即将被转换的北京时间(字符串"2014-4-4 22:00")换成 GMT 时间("2014-4-4 14:00")。如果直接把这个 NSDate(longlong,1970以来的秒数或毫秒数)传给服务器,服务器会把这个时间当成北京时间使用(实际上它却是GMT时间),这就导致时 间差了8小时。

正确的做法是在这个 NSDate 的基础上加上时差。而时差的计算需要知道当前时区。[NSTimeZonesystemTimeZone]可以得到当前时区(东8区),然后用 secondsFromGMTForDate: 方法可获得这个时区(东8区)的时差(以秒计)。代码如下:

NSDateFormatter* df=[NSDateFormatter new];

//        [dfsetLocale:[NSLocale  currentLocale]];

       df.dateFormat=@"yyyy-MM-dd HH:mm";

        NSDate* date=[dfdateFromString:@"2014-4-4 22:00"];

        NSTimeZone *zone =[NSTimeZone systemTimeZone];

        NSInteger interval = [zonesecondsFromGMTForDate: date];

        NSDate *localeDate =[date  dateByAddingTimeInterval:interval];

       NSLog(@"%@",localeDate);

103、禁止 UITableViewController 中键盘弹出动画

TableViewController 中内置了键盘弹出动画的代码,当单元格中的输入控件弹出软键盘时,tableView 会自动向上滚动。但这个功能有时候会带来大麻烦,因为有时候输入控件会被滚动到不可视的地方。由于我们无法修改框架的代码,所以这种情况下我们必须放弃使 用 TableViewController(子类化),而改用一般的UIViewController+UITableView 代替。但有时候我们必须使用TableViewContrller——例如想使用它的静态单元格,则可以通过下列方法解决。在 UITableViewController 子类中覆盖 viewWillAppear 方法,禁用父类的 viewWillAppear 行为。即不要调用[superviewWillAppear:animated]一句:

-(void)viewWillAppear:(BOOL)animated{

    // Override super method with don‘tcall [super viewWillApper]

}

104、应该在什么时候使用 NSCache

NSCache 会自动根据内存压力释放其中的某个缓存对象(例如视图被销毁,或者缓存的对象实在太多)。因此 NSCache 缓存的对象必须是可以重建的,例如这些对象——可以在需要时从网络上下载到的数据。否则,你不应当使用 NSCache——因为对象不知道什么时候就会被销毁了。

因此,使用 NSCache 时必须注意,如果检索的对象在 cache 中不存在,我们必须重建一个:

-(CachedObject)getCachedObject:(id)key{

    id* obj=[NSCacheObjectobjectForKey:key];

    if (cb==nil) {

       obj=[[CachedObjectalloc]init];  // Recreate cached object

       ……

    }

    return obj;

}

105、Pods 在 Xcode5 上Archive 的问题

问题描述:

Archive 时出现如下错误(debug 时可能是正常的):

ld: library not found for -lPods

问题是由于 Xcode5.x 现在会检测依赖项目的 architecture ,其设置必须和主项目一致,否则该依赖项目会被拒绝(即不会编译)。

解决办法:

在 Pods 项目的所有 target 下,将他们的 architecture 设置为和主项目一致。

106、如何查看一个静态库支持的 Architecture

使用“lipo -info 静态库文件”命令,例如:

lipo -info Unrar4iOS

然后终端会作如下显示:

Architectures in the fat file: Unrar4iOS are: armv7 armv6 i386

107、项目中引入某些静态库会导致在 Archive 时报“Undefined symbols forarchitecture armv7s/arm64”错误

正如问题 105 所述。除了用问题 105 中的解决方法,还有一种解决办法。

首 先查看该静态库的 Architecture(参考问题106)。然后修改 Scheme 为该静态库支持的 Architecture。然后修改 Buid Settings 中的 Build Active ArchitectureOnly(仅编译为所选的架构),将值改为 Yes。然后编译即可。

108、Autolayout 下,UITableView 的高度不正确

Autolayout 下,如果有导航栏,视图上的 UITableView 受 constraints 的限制,运行时高度被 constraints 重新设置为没有导航栏的高度。此时应该实现viewDidLayoutSubviews 方法,将 constraints 的影响排除:

- (void)viewDidLayoutSubviews {

_table.frame=CGRectMake(0,0,self.view.frame.size.width,self.view.frame.size.height);

}

109、如何修改默认返回按钮的title?

假设导航为:A视图-->B视图

如果要改变B视图返回到A视图的返回按钮的title,只需在A视图中使用如下代码:

self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:nil];

B视图不用做任何操作。

110、有一个空对象,但它既不是 nil,也不是 null?

它是 NSNull。你可以打印这个对象(用 po 命令或者 NSLog),则打印结果是“<null>”,而不是“(null)”(nil 对象)。

由于O-C 的集合对象中不允许插入空值(nil),而 NSNull 并不是 nil,所以就用NSNull 对象来表示这个集合为空(表示列表结束)。而且,与 nil 不同,发送消息给一个 NSNull 将导致异常。

NSNull具有唯一的方法:[NSNull null]你可以用它来测试一个对象是不是 NSNull:

BOOL isNSNull(id any){

    return [any isEqual:[NSNullnull]];

}

    #import "a.h"

#endif

111、为什么在 IB 中设置 layer.borderColor 无用?

我在 IB 中通过设置 UIView 的Runtime 属性,以获得一个圆角带红色边框的矩形效果,如下图所示:

技术分享

但是,borderColor 属性似乎是无效的,边框无法显示。

layer.borderColor 是一个 CGColorRef 属性,而 Runtime 属性的颜色面板中得到的只能是 UIColor 属性,因此你无法在 IB 中设置 borderColor,而只能通过代码设置。

112、在 Swift 中还可以使用 DLog 宏和 ALog 宏吗?

在C和Objective-C中使用的复杂宏无法在Swift中使用。复杂宏是除了常量定义之外的宏,包含带括号的函数宏。我们可以定义全局函数来代替:
func dLog(message: String, filename: String = __FILE__, function: String =__FUNCTION__, line: Int = __LINE__) {

    if DEBUG==1 {   

   println("[\(filename.lastPathComponent):\(line)] \(function) -\(message)")

    }

}

113、O-C 中的枚举在 Swift 中无法使用?

出错:Enum case pattern cannot match values of the non-enum type ‘枚举名‘

截止到 Beta 5,Swift 只能映射O-C 中使用 NS_ENUM 宏定义的枚举类。因此对于所有typedef enum 方式定义的 O-C 枚举,都需要重新用 NS_NUM 定义:

typedef enum {

    ChinaMobile = 0,    // MNC 00 02 07

    ChinaUnicom,        // MNC 01 06

    ChinaTelecom,       // MNC 03 04

    ChinaTietong,       // MNC 20

    Unknown

} CarrierName;

114、Swift 中不能使用 #ifdef 宏吗?

Swift 中可以使用 #if/#else/#endif:

#if DEBUG

    let a = 2

#else

    let a = 3

#endif

但是 DEBUG 符号必须在 Other Swift Flags 中进行定义:

技术分享

115、textFieldShouldReturn 方法返回 YES 或者 NO 有什么不同?

返回 YES, 将触发自动文本纠正和自动首字母大写(即苹果文档中所谓的默认行为),返回 NO则不会。

116、如何改变 UITextView 的行高?

首先设置 UITextView 的 layouManager.delegate:

textView.layoutManager.delegate = self; // you‘ll need to declare you

然后在实现 delegate 方法:

 

- (CGFloat)layoutManager:(NSLayoutManager *)layoutManagerlineSpacingAfterGlyphAtIndex:(NSUInteger)glyphIndexwithProposedLineFragmentRect:(CGRect)rect

{

    return 4; // 这是行间距

}

117、修改 UITextView 的最小高度

无 论你如何在 IB 中修改 UITextView 的frame 高度都是无用的。UITextView的最小高度是通过它的内容来计算的,它的最小高度等于单行高度。因此在 iOS 7 上可以通过设置其 textContainerInset(iOS 6 则是 contentInset)来修改最小高度:

 if (NSFoundationVersionNumber >NSFoundationVersionNumber_iOS_6_1) {

     self.textContainerInset =UIEdgeInsetsMake(4, 10, 4, 10);

} else {

    self.contentInset =UIEdgeInsetsMake(4, 10, 4, 10);

}

117、 在插入图片(NSTextAttachment)后NSAttribtuedString 字体莫名被变小

插入图片前字体:

技术分享

插入图片后:

技术分享

这是因为 NSTextAttachment 插入时是以上标形式插入的,这导致三个属性被修改:字体、baseline、 NSSuperscriptAttributeName。

因此在插入图片后,需要将这3个值改为默认(光修改字体不行):

[mutableAttrString addAttributes:

@{NSFontAttributeName:[UIFontsystemFontOfSize:16],(id)kCTSuperscriptAttributeName:@0,NSBaselineOffsetAttributeName:@0}           range:NSMakeRange(0,mutableAttrString.length)];

注意 NSSuperscriptAttributeName 在 iOS 中无效。因此需要用 CoreText 的 kCTSuperscriptAttributeName 代替。

118、如何一次性向导航控制器中压入两个 ViewController?

只需这样:

[viewController1.navigationController pushViewController:viewController2animated:NO];

[viewController2.navigationController pushViewController:viewController3animated:YES];

第一次 PUSH 时不带动画,第二次 PUSH 时带动画,这样用户会觉得只 PUSH 了一个ViewController,而实际上你 PUSH 了两个。

119、统计整个工程代码行数

打开终端,用cd命令 定位到工程所在的目录,然后调用以下命名即可把每个源代码文件行数及总数统计出来:

find . "(" -name "*.m" -or -name "*.mm" -or-name "*.cpp" -or -name "*.h" -or -name "*.rss"")" -print | xargs wc -l

 

其中,-name  "*.m" 就表示扩展名为.m的文件。同时要统计java文件和xml文件的命令分别是:

find . "(" -name "*.java"  ")" -print | xargs wc -l

find . "(" -name "*.xml"  ")" -print | xargs wc -l

 

120、如何减少 requireGestureRecognizerToFail 方法的延时?

requireGestureRecognizerToFail一般用于区别单击和双击的识别。假设你有两个手势,一个单击一个双击,你不想在用户双击时同时被识别为单击双击,可以使用下列代码:

UITapGestureRecognizer *doubleTapGestureRecognizer =[[UITapGestureRecognizer alloc]

                       initWithTarget:self action:@selector(handleDoubleTap:)];

doubleTapGestureRecognizer.numberOfTapsRequired = 2;

 

 

[self addGestureRecognizer:doubleTapGestureRecognizer];

UITapGestureRecognizer *singleTapGestureRecognizer =[[UITapGestureRecognizer alloc] initWithTarget:selfaction:@selector(handleSingleTap:)];

singleTapGestureRecognizer.numberOfTapsRequired = 1;

[singleTapGestureRecognizer requireGestureRecognizerToFail: doubleTapGestureRecognizer];

 [selfaddGestureRecognizer:singleTapGestureRecognizer];

但 是这个方法有个致命的缺点,由于每次单击都要先进行双击的识别,而且要等双击识别失败才进行单击的处理,导致每次单击都会有一个延迟,一般这个时间会是 0.35 秒。尽管 0.35 秒很短,但用户能感到明显的延迟。因此我们需要重载 TapGestureRecognizer,减少这个延时:

#import <UIKit/UIGestureRecognizerSubclass.h>

#define UISHORT_TAP_MAX_DELAY 0.25

@interface UIShortTapGestureRecognizer : UITapGestureRecognizer

@property(nonatomic,assign)float maxDelay;

@end

 #import"UIShortTapGestureRecognizer.h"

 

@implementation UIShortTapGestureRecognizer

- (instancetype)initWithTarget:(NSObject*)target action:(SEL)selector{

    self=[super initWithTarget:targetaction:selector];

    if (self) {

       self.maxDelay=UISHORT_TAP_MAX_DELAY;

    }

    return self;

}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

    [super touchesBegan:toucheswithEvent:event];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(self.maxDelay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^

                   {

                       // 0.1 秒后,宣告手势识别失败

                       if  (self.state !=UIGestureRecognizerStateRecognized)

                       {

                           self.state= UIGestureRecognizerStateFailed;

                       }

                   });

}

@end

现在可以在实例化 UIShortTapGestureRecognizer 之后设置它的 maxDelay属性了。注意,这个值不要小于 0.2秒(用户必须非常快,否则无法完成双击动作),最佳为 0.25。

121、如何将字典/数组转换为字符串?

NSString* id2json(id dicOrArr){

    NSError *error;

    NSData *jsonData =

    [NSJSONSerialization

     dataWithJSONObject:dicOrArr

    options:NSJSONWritingPrettyPrinted // Pass 0 if you don‘t care about thereadability of the generated string

     error:&error];

   

    if (! jsonData) {

        DLog(@"Got an error:%@", error);

        return nil;

    } else {

        NSString *jsonString =[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];

        return jsonString;

    }

}

122、Xcode 6.1 GM,AudioToolbox 无法使用

通常,我们在程序中习惯用 AudioToolbox 来播放较短的声音效果,例如这段代码,在 Xcode 6.0 中是可用的,但在 Xcode 6.1 GM 上会导致一个编译错误:

func playSystemSound(soundName:String){// Tink.caf

    letsoundPath:String="/System/Library/Audio/UISounds/"+soundName

    var soundID:SystemSoundID = 0

    ifNSFileManager.defaultManager().fileExistsAtPath(soundPath) {

        let soundURL =NSURL(fileURLWithPath: soundPath)

       AudioServicesCreateSystemSoundID(soundURL as CFURL, &soundID)

       AudioServicesPlaySystemSound(soundID)

    }

}

只需将 AudioServicesCreateSystemSoundID(soundURL as CFURL, &soundID) 替换为:

AudioServicesCreateSystemSoundID(soundURL, &soundID)

123、升级为 Yosemite 后,Xcode6 无法运行模拟器

升级为 Yosemite 后,Schema 中的所有模拟器消失,如下图所示,模拟器一栏仅剩下 iOS Device :

技术分享

此时,App 无法在模拟器上运行。

解 决方法为,打开模拟器(方法:Xcode -> Open Developer Tool -> iOS ),此时会弹出一个 Unable to determine device 的错误,不用管,点击 OK。在模拟器菜单,Hardware -> Device ->Manage Devices…,弹出设备列表。点击左下角的 + 按钮,添加所需要的模拟器,然后点击 Create即可,如下图所示:

技术分享

124、集成Alamofire时出现错误:only supported on iOS 8.0 andlater

将Alamofire的deployment target设置为和主项目的一样。

 

125、错误:must register a nib or a class for the identifier

当使用编程方法构建UITableViewCell时,需要注册该UITableViewCell的xib。在 viewDidLoad 方法中加入下句:

[self.yourTableViewName registerNib:[UINibnibWithNibName:@"YourNibName" bundle:nil]

        forCellWithReuseIdentifier:@"YourIdentifierForCell"];

如果UITableViewCell未使用xib,但该UITableViewCell有自定义的.m/.h文件,则使用下句替代:

[self.tableView registerClass:[UITableViewCell class]forCellReuseIdentifier::@"Cell"];

 

126、错误Undefined symbols for architecture armv7

如果你在真机调试出现这个错误,在模拟器下没有这个错误。说明有某个lib库只有x86版本,而缺少armv7/armv6版本。查看错误所指向的库,用lipo –info 命令检查该.a文件的二进制信息。

如果缺少armv6/armv7版本的.a文件,请重新编译一个armv7/armv6的.a文件添加到工程中。

还可以用lipo –create –output命令将两个版本的.a文件融合成一个通用版.a文件放到工程中。

此外,可能还需要检查Library Search Path设置,看.a文件的路径指向是否正确。

127、错误Type ‘String‘ does not conform to protocol ‘IntervalType‘

当switch语句中对String? 进行匹配时出现此错误。例如:

switch item.name {

                case "到达现场时间":

cellItems.append(item)default:

    break

}

修改为如下语句可解决此错误:

if let name = item.name{

                switch name {

                case "到达现场时间":

cellItems.append(item)default:

    break

                }

 }

128、pod update时经常出现错误:Attempt to read nonexistent folder

cocoapod服务器被墙了,请使用代理或者VPN(推荐VPN)。

 

129、ViewController类声明出现错误:initializer ‘init(coder:)‘must be provided by a subclass of UIViewController

在你的ViewController中有某个属性声明时未赋予初值,而且也没有标记为可空。例如 varkPickerViewSize:CGSize,可修改为var kPickerViewSize:CGSize?即可消除此错误

 

130、Swift中如何取得Dictionary的所有key?

在NSDicationary中可以使用allKeys属性,在Dictionary中则使用keys属性:

let array:[String] = [String](dictionary.keys)

131、如何限制ScrollView在某个方向上不可滚动?

例如,要限制x方向上不可滚动,可以实现UIScrollViewDelegate协议方法:

func scrollViewDidScroll(scrollView: UIScrollView) {

        ifabs(scrollView.contentOffset.x) > 0 {

            scrollView.contentOffset= CGPointMake(0, scrollView.contentOffset.y)

        }

}

 

132、如何在Swift Framework目标中导入O-C框架

以BmobSDK 为例(CommonCrypto等C/O-C框架也是一样的),当你将BmobSDK添加到Link Binary With Libraries中时,当你使用“importBmobSDK”语句时,出现错误:no such module

如果你试图使用bridging headers导入BmobSDK,则会导致另一个错误。

这是由于Swift Framework不支持bridging headers文件。

解决这个问题,需要经过以下步骤:
1) 在项目目录中创建BmobSDK目录,将BmobSDK.framework放到这个目录。同时在这个目录下创建一个module.map文件,内容如下:

module BmobSDK [system] {

    header"/Users/kmyhy/Documents/Swift/code/第12章/kNote/BmobSDK/BmobSDK.framework/Headers/Bmob.h"

    link "BmobSDK"

    export *

}

这将允许我们将BmobSDK当做Swift module来使用。

2) 在Build Settings中,找到Import Paths(SWIFT_INCLUDE_PATHS),增加BmobSDK目录。如下图所示:

3)在swift文件中导入BmobSDK框架:

import BmobSDK

 

133、如何在App Extension中使用CocoaPods

在 Podfile中加入此句:

link_with ‘扩展的名字‘

添加bridging header文件,并设置Objective-C BridgingHeader。

134、错误‘xxx.pch‘ has been modified since the precompiled header was builterror in Xcode

执行深度Clean(快捷键Option+Command+Shift+K)

 

135、Document Provider扩展中,DocumentPickerViewController的documentStorageURL属性返回为nil。

认容器App,Document Provider扩展和File Provider扩展的App Groups设置正确,如果有任何惊叹号,请Fixed。

136、插入和删除CollectionView中的Cell

跟 TableView一样,我们可以用insertItemsAtIndexPaths()/deleteItemsAtIndexPaths()插入/删 除单元格。不同的是CollecionView不再支持beginUpdates()/endUpdates()操作。默认已支持插入/删除动画,如果不 想要默认动画效果,可以设置UIView的animationsEnabled属性:

BOOL animationsEnabled = [UIView areAnimationsEnabled];

[UIView setAnimationsEnabled:NO];

[myCollectionView reloadItemsAtIndexPaths:myIndexPaths];

[UIView setAnimationsEnabled:animationsEnabled];

 

137、UIScrollView中的UICollectionView不会滚动

检查UIScrollView是否被设置了delegate属性,同时检查在delegate中是否实现了scrollViewDidScroll方法。如果是,请删除该方法(仅仅取消delegate属性是不行的)。

同时检查UICollectionView的宽度(或宽度约束)、高度(或高度约束),以及列表内容大小,因为当列表内容小于UICollectionView的宽(或高)时,该方向的滚动条不会出现。

138、单元格使用UITableViewCellStyleValue1,为什么显示不正确?

UITableViewCell 有几个内置的类型,如UITableViewCellStyleDefault、UITableViewCellStyleValue1、UITableViewCellStyleValue2。

这 些Cell都是SDK预先配置的,它们呈现固定的内置样式,比如字体大小、颜色、对齐方式等。如果你在想修改这些配置,很可能显示不正常,比如有的文本一 下子能显示,一下子又不显示,或者一下子显示固有字体(及颜色),一下子(比如说点击单元格)又显示修改后的字体(及颜色)。

在这种情况下,你最好定制自己的单元格(子类化)。

139、boundingRectWithSize 计算出的尺寸不正确?

注意提供正确的options参数。对于UILabel,至少需要使用UsesLineFragmentOrigin和UsesFontLeading两个选项:

CGRect paragraphRect =

  [attributedTextboundingRectWithSize:CGSizeMake(300.f, CGFLOAT_MAX)

options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)

  context:nil];

140、Swift中无法对多个枚举值进行按位或(|)运算

注意,这个问题在 iOS 8.3 SDK Beta 1 (12F5027d)中已解决。对于低于此版本的SDK,我们可以用以下代码代替:

let options =unsafeBitCast(NSStringDrawingOptions.UsesLineFragmentOrigin.rawValue |

                   NSStringDrawingOptions.UsesFontLeading.rawValue,

                   NSStringDrawingOptions.self)

               

let frame = text.boundingRectWithSize(size, options:options, attributes:D,context:nil)

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。