iOS 开发问与答(85-102)

85. 获取当前时间的几种方法

效率由高到低分别是:

  1. CACurrentMediaTime

    
    #import QuartzCore
    
    let now:Double = CACurrentMediaTime()
  2. gettimeofday

    
    #include <sys/time.h>
    
    struct timeval tv;
    gettimeofday(&tv,NULL);
  3. timeIntervalSinceReferenceDate

    NSDate.timeIntervalSinceReferenceDate()
  4. CFAbsoluteTimeGetCurrent()

    swift
    CFAbsoluteTimeGetCurrent()

  5. timeIntervalSince1970

    let now = NSDate().timeIntervalSince1970

    返回目录

86. 如何提交加急审批

如果有重大Bug需要修复,或者配合某个营销活动进行新版本发布,我们需要向苹果提交加急审批要求。
加急审批一旦通过,审批时间将大大缩短。

  1. 首先上传应用到 itunesconnect,直到看见 App 状态为等待审批。
  2. 访问地址 https://developer.apple.com/contact/app-store/?topic=expedite,提交加急审批。

    • I would like to 选择 request a expedited app review
    • Apple ID of App 填写 app 在苹果商店的 ID (不是 bundle id,你可以在 itunesconnect 的 App 信息中看到这个 10 位数字的 ID)
    • Select a reason 选择一个适当的理由,比如 Critics Bug Fix(重大缺陷修复)
    • Explanation 陈述理由,比如,发现重大 Bug,发布日期不等人等等:
    We found a serious problem,some functionally in our recent submited build is not available,poor brings to the user experience, for example, user can't enter any charactor,date or time when app need such information to run.
    
    Now we rejected that build(build 1.5.1) which is to be published and submited an emergency repair build (build 1.5.3) for this serious problem. Our release date is so near and it is very important to us, so we need a quick reply, please help us, thank you very much!

    返回目录

87. 如何判断一个 SwiftyJSON 中的某个 key 是否有值

扩展 JSON 类,增加方法:

extension JSON{
    public func exists() -> Bool{
        if let errorValue = error where errorValue.code == ErrorNotExist{
            return false
        }
        return true
    }
}

然后这样使用它:

if json["checked"].exists() && json["checked"].bool == true{
    // do sth.
    ...
}

返回目录

88. 为什么 UITableView 的 indexPathForSelectedRow 会是 nil?

如果你不小心在 didSelectRowAtIndexPath 方法中调用了 table view 的 deselectRowAtIndexPath 方法,则 indexPathForSelectedRow 会为 nil。
返回目录

89. 将字符转换成 Int

Swift 中比较麻烦,需要将字符转换成 String ,再转换成 NSString,再调用 characterAtIndex:

let ch: Character = "A"
let s = String(ch) as NSString
var singleCharValue = Int(s.characterAtIndex(0))// return 97

返回目录

90. 为什么 UIScrollView 的 scrollRectToVisible 不工作?

contentSize 计算不正确,请正确设置 UIScrollView 的 contentSize。
返回目录

91. 定制 tab bar 的 badge view

创建一个 Category,为 UITabBar 声明一个新方法:

@interface UITabBar(BadgeView)
/**
 @brief 自定义 badge view
 @param index tab bar item 索引。
 @param imageName badge view 的背景图片, 大小:18*18。
 @param fontColor badge view 上数字的颜色。
 */
- (void)customBadgeViewWithImageName:(NSString*)name fontColor:(UIColor*)fontColor;
@end

实现文件:

#import "UITabBar+BadgeView.h"

@implementation UITabBar(BadgeView)
- (void)customBadgeViewWithImageName:(NSString*)name fontColor:(UIColor*)fontColor{
    // 1 
    int i = 0;
//    NSLog(@"...................................");
    // 2
    for (UIView* tabBarButton in self.subviews) {
//        NSLog(@"..........%d th tabBarButton",i);
        // 3
        UIView* badgeBackground;
        NSString* badgeValue;
        // 4 
        for (UIView* badgeView in tabBarButton.subviews) {
            NSString* className = NSStringFromClass([badgeView class]);

              // NSLog(@".........subview:%@",className);
            // 5
            if ([className rangeOfString:@"BadgeView"].location != NSNotFound) {
                NSLog(@"%@",badgeView);
                // 6
                badgeBackground = nil;
                badgeValue = nil;
                // 7
                for (UIView* badgeSubview in badgeView.subviews) {
                    NSString* className = NSStringFromClass([badgeSubview class]);

                    // looking for _UIBadgeBackground
                    if ([className rangeOfString:@"BadgeBackground"].location != NSNotFound) {
                        badgeBackground = badgeSubview;
                    }
                    if ([badgeSubview isKindOfClass:[UILabel class]]) {
                        ((UILabel *)badgeSubview).textColor = fontColor;
                        badgeValue = ((UILabel*)badgeSubview).text;
                    }
                }

            }
        }
        // 8
        if(badgeBackground != nil && [badgeValue isEqualToString:@" "]){
            @try {
                [badgeBackground setValue:[UIImage imageNamed:name] forKey:@"image"];
            }
            @catch (NSException *exception) {}
        }
        i++;
    }
//    NSLog(@"...................................");
}
@end

上述代码解释如下:

  1. i 仅用于计数,让控制台输出时更好看点。
  2. 迭代 tab bar 的 subview。注意这些 subview 中其实并不完全是 5 个 tabBarButton,还有额外的两个 view。

    2016-10-21 16:49:01.542 Youxin[5375:153756] ...................................
    2016-10-21 16:49:01.542 Youxin[5375:153756] ..........0 th tabBarButton
    2016-10-21 16:49:01.542 Youxin[5375:153756] .........subview:_UIBackdropView
    2016-10-21 16:49:01.543 Youxin[5375:153756] ..........1 th tabBarButton
    2016-10-21 16:49:01.543 Youxin[5375:153756] .........subview:UITabBarSwappableImageView
    2016-10-21 16:49:01.543 Youxin[5375:153756] .........subview:UITabBarButtonLabel
    2016-10-21 16:49:01.543 Youxin[5375:153756] .........subview:_UIBadgeView
    2016-10-21 16:49:01.543 Youxin[5375:153756] <_UIBadgeView: 0x78fbb710; frame = (36 2; 18 18); userInteractionEnabled = NO; layer = <CALayer: 0x78fbb7f0>>
    2016-10-21 16:49:01.544 Youxin[5375:153756] ..........2 th tabBarButton
    2016-10-21 16:49:01.544 Youxin[5375:153756] .........subview:UITabBarSwappableImageView
    2016-10-21 16:49:01.544 Youxin[5375:153756] .........subview:UITabBarButtonLabel
    2016-10-21 16:49:01.544 Youxin[5375:153756] .........subview:_UIBadgeView
    2016-10-21 16:49:01.544 Youxin[5375:153756] <_UIBadgeView: 0x790b8230; frame = (36 2; 24 18); userInteractionEnabled = NO; layer = <CALayer: 0x790b7910>>
    2016-10-21 16:49:01.545 Youxin[5375:153756] ..........3 th tabBarButton
    2016-10-21 16:49:01.545 Youxin[5375:153756] .........subview:UITabBarSwappableImageView
    2016-10-21 16:49:01.545 Youxin[5375:153756] .........subview:UITabBarButtonLabel
    2016-10-21 16:49:01.545 Youxin[5375:153756] ..........4 th tabBarButton
    2016-10-21 16:49:01.545 Youxin[5375:153756] .........subview:UITabBarSwappableImageView
    2016-10-21 16:49:01.545 Youxin[5375:153756] .........subview:UITabBarButtonLabel
    2016-10-21 16:49:01.545 Youxin[5375:153756] ..........5 th tabBarButton
    2016-10-21 16:49:01.546 Youxin[5375:153756] ..........6 th tabBarButton
    2016-10-21 16:49:01.546 Youxin[5375:153756] .........subview:UITabBarSwappableImageView
    2016-10-21 16:49:01.546 Youxin[5375:153756] .........subview:UITabBarButtonLabel
    2016-10-21 16:49:01.546 Youxin[5375:153756] ...................................
    

    在上面的 Log 中,依次输出了 0-6 共 7 个 subview,其中 tabBarButton 是从 1-5 的 5 个。

  3. 声明两个变量,一个用于在 dump subview 时存储找到的 BadgeBackground 对象(即小红点的 UIImageView)。一个用于找到 badge 上的字符串(即小红点上的数字,但也不完全是数字,也有一个特殊字符“空格”,用于表示这个小红点需要定制化。

  4. 这个迭代用于找到 _BadgeView,我们要替换的图片就在 _BadgeView 的 subview 中。注意,不是每个 tabBarButton 都会有 _BadgeView。只有设置过 badge 值(即 badgeValue 不为空的 tabBarButton)的才会有 _BadgeView。
  5. 通过类名和字符串比较,找到 _BadgeView,并在控制台中输出(见上面的控制台输出)。
  6. 清空两个变量,避免受上次查找结果的影响。
  7. 对 _BadgeView 的 subview 进行迭代,在这个迭代中,找到我们想要替换掉的图片(即 BadgeBackground),以及 UILabel。将 BadgeBackground 和 UILabel 的 text 赋给两个外部变量。
  8. 当 tabBarButton 的 subview 遍历结束,我们想要的两个对象要么找到,要么找不到(因为这个 tabBarButton 没有 Badge 值不需要显示红点)。如果找到,我们比较 badge 值,如果这个 badge 值是个“空格”(或者其他预先约定的字符),说明这个小红点的样式需要定制,将小红点的背景图片替换成我们制定的图片(18*18 大小)。

然后这样每当修改过 tab bar 上的 badge 时都需要调用这个方法。最好是另外编写一个用于修改 tab bar badge 的 方法比如 setTabBadge,每次要修改 badge 值时统一通过setTabBadge方法来进行:

func setTabBadge(index:Int,number:Int){
        if tabbarController != nil {
            if let vc = tabbarController!.viewControllers![index] as? UIViewController {
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    switch index{
                    case 0,2,3,4: // 小红点
                        vc.tabBarItem.badgeValue = number > 0 ? " " : nil
                        self.tabbarController!.tabBar.customBadgeViewWithImageName("reddot", fontColor: UIColor.whiteColor())
                    case 1: // 大红点
                        vc.tabBarItem.badgeValue = number > 99 ? "···" : "\(number)"
                        self.tabbarController!.tabBar.customBadgeViewWithImageName("reddot", fontColor: UIColor.whiteColor())
                    default:
                        ()
                    }
                })
            }
        }
    }

注意,如果没有使用统一的 setTabBadge 修改 badge 值,或者修改 badge 值后没有调用 customBadgeViewWithImageName 方法,则定制的效果又会失效。
返回目录

92. 为什么 TabBarController.viewControllers 中的 ViewController 不会调用 viewDidLoad 方法?

你需要在 TabBarController 子类的 viewDidLoad 方法中手动触发 viewControllers 的 viewDidLoad 方法,否则它们的 viewDidLoad 方法不会被调用(一直到有用户点击 tab bar 显示这个 ViewController):

class MainViewController : UITabBarController {

    override func viewDidLoad() {
        super.viewDidLoad()
        for viewController in self.viewControllers!
        {
            // 引用 viewController 的 view 属性将会触发 viewDidLoad 方法
            let v = viewController.view; 
        }
    }
}

注意如果 viewController 中不仅仅包含了 UIViewController,也可能包含 UINavigationController,则这种情况下需要区别处理:

if let nc = viewController as? UINavigationController,
        vc = navctr.viewControllers[0] as? UIViewController {
    let v = vc.view;
}

返回目录

93. 为什么设置 navigationBarHidden = true(隐藏导航条) 不生效?

经测试,某些情况下只能在 viewDidAppear 中设置 navigationBarHidden,而在 viewDidLoad 和 viewWillAppear 方法中设置是无效的(不知道为什么?)。所以请在 viewDidAppear 方法中设置 navigationBarHidden = true 来隐藏导航条。但是这会带来一个坏处,因为 viewDidAppear 调用时 view 已经显示,因此 navigationBar 会短暂出现一下然后消失(不论 animated 选项设为 true 或 false)。

要解决这个问题需要用到 UINavigationControllerDelegate。首先在 ViewController 中声明一个变量:var oldNavDelegate:UINavigationControllerDelegate?,然后在 viewDidLoad 中:

oldNavDelegate = navigationController?.delegate
navigationController?.delegate = self

即将 navigationController 的 delegate 换成当前 ViewController,同时将原来的 delegate 保存到 oldNavDelegate 里。

然后实现 UINavigationControllerDelegate:

func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
    navigationController.navigationBarHidden = true
}

最后在 viewWillDisappear 中将 delegate 恢复原值:

override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
    navigationController?.delegate = oldNavDelegate
}

返回目录

94. 如何判断一个 Array 或 Dictionary 中包含了空值?

用 is NSNull 进行判断:

// 数组中包含空值
if data[0] is NSNull {
    ...
}

// 字典中包含空
if data["key"] is NSNull {
    ...
}

返回目录

95. 在使用自动布局的情况下,设置 cell 的 indentationLevel 不生效

可以修改相关 NSLayoutConstraint 的 constant 属性来实现缩进。

例如:


class ByDeptCell: UITableViewCell {
    ... ...
    @IBOutlet weak var iconLeft:NSLayoutConstraint!

    var deep:Int = 0 {
        didSet{
            iconLeft.constant = CGFloat(deep * 10 + 8)
        }
    }
    ... ...
}

在 cellForRowAtIndexPath 方法中:

cell.deep = 2

返回目录

96. 如何使 Button 的文本左对齐?

button.contentHorizontalAlignment = .Left;

返回目录

97.如何以引用方式(指针)使用结构体?

有时候我们必须将结构体(比如一个 JSON 对象)传递给一个方法,在方法内部对其进行修改(比如设置某个属性),但结构体是按值传递的,正常情况下,传递到方法内的结构体会被复制,即生成一个新的 structure 实例,你的修改是无效的。

这种情况下必须使用 UnsafeMutablePointer 来声明参数。在参数列表中将 structure 参数声明为 UnsafeMutablePointer类型 :

func setNodeOpen(nodeRef:UnsafeMutablePointer<JSON>,open:Bool,id:String){
    ... ....
}

调用方法时,使用 & 取地址运算符:

setNodeOpen(&tree!,open:open,id:id)

在上述方法实现中,要访问结构体的成员,或者修改结构体的属性,使用 memory 属性:

    func setNodeOpen(nodeRef:UnsafeMutablePointer<JSON>,open:Bool,id:String){
        if id == nodeRef.memory["id"].string {
            nodeRef.memory["open"].string = open == true ? "true" : "false"
            nodeOpenStatusChanged = true // 标志:设置 open 成功,不再递归
        }else {
            if nodeOpenStatusChanged == false {// 如果没找到指定 id,继续递归
                for i in 0..<nodeRef.memory["children"].count {
                    setNodeOpen(&nodeRef.memory["children"][i],open: open,id: id)
                }
            }
        }
    }

返回目录

98. 如何指定 NSDate 的小时或分钟?

如果你想将一个 NSDate 修改为指定的时间,比如根据当前时间生成一个 NSDate,但需要将小时和分钟修改为 8:30,那么可能需要以下函数:

// 设置 NSDate 的时分秒并以新 NSDate 返回
    func getDate(date:NSDate,h:Int,m:Int,s:Int)->NSDate? {
        let cal = NSCalendar.currentCalendar()

        let components = cal.components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay, fromDate: date) //初始化目标时间(以当前时间)

        components.hour = h
        components.minute = m
        components.second = s

        return cal.dateFromComponents(components)
    }

调用方法:

// 上班打卡时间为 8:30
if let beginDate = getDate(NSDate(),h:8,m:30,s:0) 

返回目录

99. 如何设置 Text Field 文字左边的 Padding?

左边留白 5 个像素:

myTextField.layer.sublayerTransform = CATransform3DMakeTranslation(5, 0, 0);

返回目录

100. 如何在Swift 中访问 O-C 的枚举?

Swift 自动对 O-C 中以 NS_ENUM 定义的枚举进行桥接,因此必须将 O-C中的枚举修改为用 NS_ENUM 宏来定义:

swift
typedef NS_ENUM(NSUInteger, CustomBadgeType){
kCustomBadgeStyleRedDot = 0,
kCustomBadgeStyleNumber = 1,
kCustomBadgeStyleNone = 2
};

在 Swift 中,就可以通过如下方式访问枚举:

swift
CustomBadgeType.StyleRedDot

返回目录

101.Swift 中如何合并多个枚举值?

let options = unsafeBitCast(NSStringDrawingOptions.UsesLineFragmentOrigin.rawValue | 
                            NSStringDrawingOptions.UsesFontLeading.rawValue,
                            NSStringDrawingOptions.self)

返回目录

102. 为什么 UIView 动画不以动画的方式执行?

我们让以下动画块以动画方式执行:

UIView.animateWithDuration(0.75, delay: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: { () -> Void in
            self.iv.snp_updateConstraints({ (make) -> Void in
                make.left.equalTo(-SCREEN_WIDTH)
            })
            self.newIv.snp_updateConstraints({ (make) -> Void in
                make.left.equalTo(0)
            })
            }, completion: {(Bool) -> Void in
})

这是因为动画块中,当你用 snapkit 引擎修改视图的 frame 后,没有通知 App 及时绘制 UI。你需要在每次修改 frame 后调用 setNeedsLayout/layoutIfNeeded 方法:

UIView.animateWithDuration(0.75, delay: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: { () -> Void in
            self.iv.snp_updateConstraints({ (make) -> Void in
                make.left.equalTo(-SCREEN_WIDTH)
            })
            self.newIv.snp_updateConstraints({ (make) -> Void in
                make.left.equalTo(0)
            })
            self.view.setNeedsLayout()
            self.view.layoutIfNeeded()
            }, completion: {(Bool) -> Void in
        })

返回目录

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值