PART 1

本文深入探讨Swift中的单例模式实现细节,并介绍了如何正确修改UIView的frame属性以避免编译错误。此外,还提供了生成随机颜色的方法,展示了如何通过手势控制动画以提升用户体验,并分享了自定义UIAlertController样式的技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Swift单例模式

Class Manager {
    //不清楚的是let是否能保证线程安全
    static let sharedManager = Manager()
    private init() {
    }
}

为什么要”中转”

想必我们都有过这样的经历,在使用initFrame:初始化视图(本例中使用UIView)后,使用下面的方式修改frame编译器会报错——Expression is not assignable

self.view.frame.size = CGSizeZero;
self.view.frame.size.height = 100f;
......

self.view.frameObjective-C访问属性时点语法的使用,本质是消息的传递,等效于:

[[self view] frame]

由于frame属性是CGRect结构体,所以frame.size.height其实是使用C语言中对结构体访问的语法,表示访问CGRect结构体中的类型为CGSize结构体的size成员变量的height字段(成员变量),等效于:

[[self view] frame].size.height = 100f;

因为Objective-C是对C的扩展,所以上面这句话会被转换成C语言的函数调用形式,类似于:

getframe(view).size.height = 100f;

在C语言中,函数返回值是一个R-Value,是不能直接给它赋值的(所谓R-Value,就是只能出现在等号的右边,你可以理解成是一个常量;而可以被赋值的是L-Value,可以出现在等号的左边,通常是变量))。因此,当你打算直接给函数的返回值赋值的时候,编译器告诉你”这个表达式无法被赋值”。这就是这个错误的出现原因。

所以就有了下面的写法,让一个临时变量作为“中转”:

// 1. 用一个临时变量保存返回值。
CGRect temp = self.view.frame;

// 2. 给这个变量赋值。因为变量都是L-Value,可以被赋值
temp.size.height = 100f;

// 3. 修改frame的值
self.view.frame = temp;//此时是通过frame的setter方法设置新的值

注:以上参考整理自segmentfault

多彩的“世界”

arc4random()这个函数可以帮我们生成非负随机整型数,但是它的范围是不可控的。相比之下, arc4random_uniform(X) 能够生成范围限定在0 ~ X-1的非负随机整型数使用得更广泛,其中之一就是生成随机颜色

UIColor *randomColor = [UIColor colorWithRed:arc4random_uniform(256) / 255.0 green:arc4random_uniform(256) / 255.0 blue:arc4random_uniform(256) / 255.0 alpha:1.0];

此方法需要四个浮点数,分别是(float)red, (float)green, (float)blue, (float)alpha。彩色显示器最常用的颜色空间RGB彩色模型,RGB(红绿蓝)依据人眼识别的颜色定义出的空间(三原色),可表示大部分颜色。每一个颜色分量的取值范围是0 ~ 255,alpha表示的是最终合成颜色的透明度。在此方法中,这四个浮点数的范围是0 ~ 1,因此我们需要将它们归一化处理之后才能使用。于是就出现了上面这样的式子,当然透明度也可以如此表示,只是透明“过头”的颜色就是画蛇添足了:)

手势控制动画

无疑,动画极大程度上提升了App的用户体验,特别是Facebook所开发出的一系列动画框架,它所展现出的动画效果真是让人“心痒”。的确,使用框架来实现想要的效果来得容易,也“去”得容易,终究还是自己掌握的用得舒坦。

我们都用过UINavigationController,原生的导航控制器可以通过视图侧滑左边缘让其子视图出栈并动态过渡到上一级视图,这其实是可交互过渡效果。同样的,动画也能通过手势来控制动画的执行,也就是可交互性动画,Android手机的主页的各个页面的切换效果就是它的典型代表。

技术的关键有两点,分别是修改执行动画图层的speed属性和timeOffset属性:

  • speed:是指动画的执行速度,它是和动画的duration属性相关联的,动画的实际执行时间可以理解为:(1 / speed)* duration,默认值为1。duration的默认值是0,但是它所代表的意思是以默认动画时间0.25s来执行动画;
  • timeOffset:是指动画的执行进程,它可以使动画快进或者回退到指定的百分点,如果它的值为0.5,即表明动画将从整体进程的一半开始执行。

有了上面的了解,不难知道将图层的speed属性值设为0,让timeOffset属性值随着手势的进行而改变就可以施展一个“小诡计”,让用户误认为是自己控制了动画的进行。下面是一个小Demo:

#import "ViewController.h"

@interface ViewController () {
    CALayer *_aniLayer;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    _aniLayer = [CALayer layer];
    _aniLayer.frame = CGRectMake(0, 0, 50, 50);
    _aniLayer.backgroundColor = [UIColor redColor].CGColor;
    _aniLayer.speed = 0.0f;

    [self.view.layer addSublayer:_aniLayer];

    //position refers to the CAlayer's center
    CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"position.x"];
    ani.toValue = @150;
    ani.duration = 2.0f;

    [_aniLayer addAnimation:ani forKey:@"position"];


    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];

    [self.view addGestureRecognizer:panGesture];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (void)handlePan:(UIPanGestureRecognizer *)pan {
    CGFloat x = [pan translationInView:self.view].x;

    x /= 150;

    CFTimeInterval timeOffset = _aniLayer.timeOffset;
    timeOffset = MIN(1.0f, MAX(0.0f, timeOffset + x));
    _aniLayer.timeOffset = timeOffset;

    [pan setTranslation:CGPointZero inView:self.view];
}

@end

改变UIAlertController的各项颜色

系统提供的单调的UIAlertController默认样式有时不能满足我们的需求,我们可能需要定制一些内容,通常是颜色或者尺寸。但就Apple暴露出来的接口我们几乎不能达到此目的,一个暴力彻底的方式就是完全来自定义,省心又省事!但我们不想盲目的派生子类,就没别的办法了吗?答案当然是否定的!通过runtime、KVC其实就可以达成我们的目的。

  1. 获取变量列表(成员变量及属性):
- (void)getIvarList {
    unsigned int count = 0;
    NSString *key = nil;

    Ivar *ivars = class_copyIvarList([UIAlertAction class], &count);

    //Ivar *ivars = class_copyIvarList([UIAlertController class], &count);

    for (int i =0; i < count; i ++) {
        Ivar thisIvar = ivars[i];

        key = [NSString stringWithUTF8String:ivar_getName(thisIvar)];

        NSLog(@"%@", key);
    }

    free(ivars);
}

//useful information about custom
//UIAlertController/UIAlertAction
//_attributedTitle、_attributedMessage....../_titleTextColor......

然后:

NSMutableAttributedString *alertControllerTitle = [[NSMutableAttributedString alloc] initWithString:@"颜色测试"];
    [alertControllerTitle addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, 2)];
    [alertControllerTitle addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:14.0f] range:NSMakeRange(0, 2)];

    NSMutableAttributedString *alertControllerMessage = [[NSMutableAttributedString alloc] initWithString:@"我想试试能改变颜色不!"];
    [alertControllerMessage addAttribute:NSForegroundColorAttributeName value:[UIColor purpleColor] range:NSMakeRange(0, 6)];

UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleAlert];
    //alert.view.backgroundColor = [UIColor orangeColor];
    //alert.view.tintColor = [UIColor purpleColor];
    [alert setValue:alertControllerTitle forKey:@"attributedTitle"];
    [alert setValue:alertControllerMessage forKey:@"attributedMessage"];

    UIAlertAction *cancleAction = [UIAlertAction actionWithTitle:@"cancle" style:UIAlertActionStyleCancel handler:nil];
    [cancleAction setValue:[UIColor orangeColor] forKey:@"titleTextColor"];

    UIAlertAction *sureAction = [UIAlertAction actionWithTitle:@"sure" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"I am sure");
    }];
    [sureAction setValue:[UIColor greenColor] forKey:@"titleTextColor"];
    //[sureAction setValue:[UIColor greenColor] forKey:@"imageTintColor"];

    [alert addAction:cancleAction];
    [alert addAction:sureAction];

    [self presentViewController:alert animated:YES completion:^{
        NSLog(@"showed alert");
    }];

就可以得到我们想要的了:

这里写图片描述

于2017-7-2号补:
如果你想访问到这个标题和内容的标签UILabel的话,可以这么做:

//实例化alertcontroller之后,可以遍历view的子视图,子视图title和message嵌套了多层父视图.(需要遍历6层subViews)
//下面是取title和message的父视图的代码:

UIView *subView1 = alert.view.subviews[0];
UIView *subView2 = subView1.subviews[0];
UIView *subView3 = subView2.subviews[0];
UIView *subView4 = subView3.subviews[0];
UIView *subView5 = subView4.subviews[0];

//取title和message:
UILabel *title = subView5.subviews[0];
UILabel *message = subView5.subviews[1];

//然后设置message内容居左:
message.textAlignment = NSTextAlignmentLeft; 

参见自CocoaChina

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值