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