Posts by Ausin

iOS开发者,目前在一家小公司做iOS负责人。 希望找到志同道合的人一起做好玩的产品。

Vapor本地搭建记录

Vapor本地搭建记录

1、homebrew版本过老导致以下问题

➜  ZXServer git:(main) brew install vapor/tap/vapor
Traceback (most recent call last):
	11: from /usr/local/Homebrew/Library/Homebrew/brew.rb:23:in `<main>'
	10: from /usr/local/Homebrew/Library/Homebrew/brew.rb:23:in `require_relative'
	 9: from /usr/local/Homebrew/Library/Homebrew/global.rb:37:in `<top (required)>'
	 8: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
	 7: from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
	 6: from /usr/local/Homebrew/Library/Homebrew/os.rb:3:in `<top (required)>'
	 5: from /usr/local/Homebrew/Library/Homebrew/os.rb:21:in `<module:OS>'
	 4: from /usr/local/Homebrew/Library/Homebrew/os/mac.rb:58:in `prerelease?'
	 3: from /usr/local/Homebrew/Library/Homebrew/os/mac.rb:24:in `version'
	 2: from /usr/local/Homebrew/Library/Homebrew/os/mac.rb:24:in `new'
	 1: from /usr/local/Homebrew/Library/Homebrew/os/mac/version.rb:26:in `initialize'
/usr/local/Homebrew/Library/Homebrew/version.rb:368:in `initialize': Version value must be a string; got a NilClass () (TypeError)

解决办法,更新homebrew:

brew update-reset

动态替换iOS app图标

动态替换iOS app图标

步骤:

1、准备两张想要替换的图,不要放到asset中,记着放到文件夹中,如下图:

2、在info.plist文件中添加相关配置:

<key>CFBundleIcons</key>
	<dict>
		<key>CFBundleAlternateIcons</key>
		<dict>
			<key>AppIcon-2</key>
			<dict>
				<key>CFBundleIconFiles</key>
				<array>
					<string>AppIcon-2</string>
				</array>
			</dict>
		</dict>
	</dict>

3、设置代码:

判断节日时间(非必需):

- (BOOL)checkTodayIsFestival {
    NSDate *date = [NSDate new];
    NSString *beginDateString = @"20210208 00:00:00";
    NSString *endDateString = @"20210220 23:59:59";
    NSDateFormatter *format = [[NSDateFormatter alloc] init];
    [format setDateFormat:@"yyyyMMdd HH:mm:ss"];
    NSDate *beginDate = [format dateFromString:beginDateString];
    NSDate *endDate = [format dateFromString:endDateString];
    if ([date isLaterThanDate:beginDate] && [date isEarlierThanDate:endDate]) {
        [NSNotificationCenter.defaultCenter postNotificationName:kChangeSkinNotification object:nil];
        return YES;
    } else {
        return NO;
    }
}

设置图标:

- (void)checkNeedChangeAppIcon {
    if (@available(iOS 10.3, *)) {
        if ([UIApplication sharedApplication].supportsAlternateIcons) {
            NSString *currentIconName = [UIApplication sharedApplication].alternateIconName;
            if ([AppConfig.shared checkTodayIsFestival]) {
                [[UIApplication sharedApplication] setAlternateIconName:@"AppIcon-2" completionHandler:^(NSError * _Nullable error) {
                    if (error) {
                        MMLog(@"change icon error: %@", error.localizedDescription);
                    }
                }];
            } else {
                if (!currentIconName || (currentIconName && [currentIconName isEqualToString:@"AppIcon-2"])) {
                    [[UIApplication sharedApplication] setAlternateIconName:nil completionHandler:^(NSError * _Nullable error) {
                        if (error) {
                            MMLog(@"change icon error: %@", error.localizedDescription);
                        }
                    }];
                }
            }
        }
    }
}

4、把弹框解决掉(非必需):

通过runtime动态处理:

#import "UIViewController+Present.h"
#import <objc/runtime.h>

@implementation UIViewController (Present)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method presentM = class_getInstanceMethod(self.class, @selector(presentViewController:animated:completion:));
        Method presentSwizzlingM = class_getInstanceMethod(self.class, @selector(mm_presentViewController:animated:completion:));
        // 交换方法实现
        method_exchangeImplementations(presentM, presentSwizzlingM);
    });
}

- (void)mm_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
    if ([viewControllerToPresent isKindOfClass:[UIAlertController class]]) {
        NSLog(@"title : %@",((UIAlertController *)viewControllerToPresent).title);
        NSLog(@"message : %@",((UIAlertController *)viewControllerToPresent).message);
        
        UIAlertController *alertController = (UIAlertController *)viewControllerToPresent;
        //都是空的时候认为是换图标的弹框,不弹了
        if (alertController.title == nil && alertController.message == nil && [AppConfig.shared checkTodayIsFestival]) {
            return;
        } else {
            [self mm_presentViewController:viewControllerToPresent animated:flag completion:completion];
            return;
        }
    }
    
    [self mm_presentViewController:viewControllerToPresent animated:flag completion:completion];
}

@end

swift中对与delegate的使用

swift中对与delegate的使用

为了保证delegate使用后不会导致内存溢出,可以做如下两种方式:

1、protocol声明时不指出base class:

protocol ZXSegmentControlDelegate {
    func didSelect(index:NSInteger)
}

使用时就需要手动将delegate置为nil

self.segmentCtl?.delegate = nil

2、protocol声明时指出base class:

protocol ZXSegmentControlDelegate: UIViewController {
    func didSelect(index:NSInteger)
}

class ZXSegmentControl: UIView {
    weak var delegate:ZXSegmentControlDelegate?

}

Apple watch开发时无法被Xcode识别的情况处理

Apple watch开发时无法被Xcode识别的情况处理

一、现象

能看到apple watch,但在Xcode的设备列表中显示为unknown

二、处理

1、删除目录:~/Library/Developer/Xcode/watchOS DeviceSupport

2、clean Xcode

3、restart macOS

小记:

之前查了很多方法,重新匹配了很多次不好使,但是没有尝试重启系统。第二天来上班启动系统才发现好了。。。

隐私策略

本应用尊重并保护所有使用服务用户的个人隐私权。为了给您提供更准确、更有个性化的服务,本应用会按照本隐私权政策的规定使用和披露您的个人信息。但本应用将以高度的勤勉、审慎义务对待这些信息。除本隐私权政策另有规定外,在未征得您事先许可的情况下,本应用不会将这些信息对外披露或向第三方提供。本应用会不时更新本隐私权政策。 您在同意本应用服务使用协议之时,即视为您已经同意本隐私权政策全部内容。本隐私权政策属于本应用服务使用协议不可分割的一部分。

适用范围

(a) 在您注册本应用帐号时,您根据本应用要求提供的个人注册信息;

(b) 在您使用本应用网络服务,或访问本应用平台网页时,本应用自动接收并记录的您的浏览器和计算机上的信息,包括但不限于您的IP地址、浏览器的类型、使用的语言、访问日期和时间、软硬件特征信息及您需求的网页记录等数据;

(c) 本应用通过合法途径从商业伙伴处取得的用户个人数据。

您了解并同意,以下信息不适用本隐私权政策:

(a) 您在使用本应用平台提供的搜索服务时输入的关键字信息;

(b) 本应用收集到的您在本应用发布的有关信息数据,包括但不限于参与活动、成交信息及评价详情;

(c) 违反法律规定或违反本应用规则行为及本应用已对您采取的措施。

信息使用

(a)本应用不会向任何无关第三方提供、出售、出租、分享或交易您的个人信息,除非事先得到您的许可,或该第三方和本应用(含本应用关联公司)单独或共同为您提供服务,且在该服务结束后,其将被禁止访问包括其以前能够访问的所有这些资料。

(b) 本应用亦不允许任何第三方以任何手段收集、编辑、出售或者无偿传播您的个人信息。任何本应用平台用户如从事上述活动,一经发现,本应用有权立即终止与该用户的服务协议。

(c) 为服务用户的目的,本应用可能通过使用您的个人信息,向您提供您感兴趣的信息,包括但不限于向您发出产品和服务信息,或者与本应用合作伙伴共享信息以便他们向您发送有关其产品和服务的信息(后者需要您的事先同意)。

信息披露

在如下情况下,本应用将依据您的个人意愿或法律的规定全部或部分的披露您的个人信息:

(a) 经您事先同意,向第三方披露;

(b)为提供您所要求的产品和服务,而必须和第三方分享您的个人信息;

(c) 根据法律的有关规定,或者行政或司法机构的要求,向第三方或者行政、司法机构披露;

(d) 如您出现违反中国有关法律、法规或者本应用服务协议或相关规则的情况,需要向第三方披露;

(e) 如您是适格的知识产权投诉人并已提起投诉,应被投诉人要求,向被投诉人披露,以便双方处理可能的权利纠纷;

(f) 在本应用平台上创建的某一交易中,如交易任何一方履行或部分履行了交易义务并提出信息披露请求的,本应用有权决定向该用户提供其交易对方的联络方式等必要信息,以促成交易的完成或纠纷的解决。

(g) 其它本应用根据法律、法规或者网站政策认为合适的披露。

信息存储和交换

本应用收集的有关您的信息和资料将保存在本应用及(或)其关联公司的服务器上,这些信息和资料可能传送至您所在国家、地区或本应用收集信息和资料所在地的境外并在境外被访问、存储和展示。

Cookie的使用

(a) 在您未拒绝接受cookies的情况下,本应用会在您的计算机上设定或取用cookies ,以便您能登录或使用依赖于cookies的本应用平台服务或功能。本应用使用cookies可为您提供更加周到的个性化服务,包括推广服务。

(b) 您有权选择接受或拒绝接受cookies。您可以通过修改浏览器设置的方式拒绝接受cookies。但如果您选择拒绝接受cookies,则您可能无法登录或使用依赖于cookies的本应用网络服务或功能。

(c) 通过本应用所设cookies所取得的有关信息,将适用本政策。

信息安全

(a) 本应用帐号均有安全保护功能,请妥善保管您的用户名及密码信息。本应用将通过对用户密码进行加密等安全措施确保您的信息不丢失,不被滥用和变造。尽管有前述安全措施,但同时也请您注意在信息网络上不存在“完善的安全措施”。

(b) 在使用本应用网络服务进行网上交易时,您不可避免的要向交易对方或潜在的交易对

7.本隐私政策的更改

(a)如果决定更改隐私政策,我们会在本政策中、本公司网站中以及我们认为适当的位置发布这些更改,以便您了解我们如何收集、使用您的个人信息,哪些人可以访问这些信息,以及在什么情况下我们会透露这些信息。

(b)本公司保留随时修改本政策的权利,因此请经常查看。如对本政策作出重大更改,本公司会通过网站通知的形式告知。

方披露自己的个人信息,如联络方式或者邮政地址。请您妥善保护自己的个人信息,仅在必要的情形下向他人提供。如您发现自己的个人信息泄密,尤其是本应用用户名及密码发生泄露,请您立即联络本应用客服,以便本应用采取相应措施。

关于iOS的推送证书

关于iOS的推送证书

Development Push SSL Certificate ,与Production Push SSL Certificate 区别在于一个是用于开发的推送证书,一个是用于发布产品的推送证书。两个证书获取到的终端deviceToken是不一样的,用两个证书生成的P12证书用于JAVA后台连接APNS的服务器地址也是不同的,Development Push SSL Certificate 对应连接的服务器地址是:gateway.sandbox.push.apple.com。Production Push SSL Certificate 对应连接的服务器地址是:gateway.push.apple.com

关于HitTest在一个view点击时会被调用两次的官方解释

关于HitTest在一个view点击时会被调用两次的官方解释

Yes, it’s normal. The system may tweak the point being hit tested between the calls. Since hitTest should be a pure function with no side-effects, this should be fine.

///

是的,这是正常的。系统可能会在调用之间调整被测试的点。由于hitTest应该是一个没有副作用的纯函数,所以这应该没问题。

RxSwift使用小结

【本文适用于有一定swift基础的同学】

1、按钮点击

tapButton.rx.tap.subscribe({ [weak self] _ in
			guard let this = self else {
				return
			}
			guard let text = this.numberLabel.text else {
				return
			}
			guard let number = Int(text) else {
				return
			}
			this.numberLabel.text = String(number+1)
		}).addDisposableTo(disposeBag)

2、按钮长按

let longPressGesture = UILongPressGestureRecognizer()
		longPressGesture.rx.event
			.subscribe(onNext: { [weak self] _ in
				guard let this = self else {
					return
				}
				guard let text = this.numberLabel.text else {
					return
				}
				guard let number = Int(text) else {
					return
				}
				this.numberLabel.text = String(number+1)
			}).addDisposableTo(disposeBag)
		self.tapButton.addGestureRecognizer(longPressGesture)
		
		tapButton.rx.tap
			.subscribe(onNext: { [weak self] in
				guard let this = self else {
					return
				}
				guard let text = this.numberLabel.text else {
					return
				}
				guard let number = Int(text) else {
					return
				}
				this.numberLabel.text = String(number+1)
			}).addDisposableTo(disposeBag)
		
		resetButton.rx.tap
			.subscribe(onNext: { [weak self] in
				guard let this = self else {
					return
				}
				this.numberLabel.text = "0"
			}).addDisposableTo(disposeBag)

3、UISlider

slider.rx.value.subscribe({
			[weak self] _ in
			guard let this = self else {
				return
			}
			this.calculator()
		}).addDisposableTo(disposeBag)

4、UITextField

numberTextField.rx.controlEvent(.touchDown).subscribe({
			[weak self] _ in
			guard let this = self else {
				return
			}
			this.numberTextField.text = ""
		}).addDisposableTo(disposeBag)

5、Swipe

let gesture = UISwipeGestureRecognizer()
		gesture.direction = UISwipeGestureRecognizerDirection.down
		
		_ = gesture.rx.event.subscribe({
			[weak self] _ in
			guard let this = self else {
				return
			}
			this.view.endEditing(true)
		})
		
		view.addGestureRecognizer(gesture)

未完待续。。。