當你一個小改變要更新cell的時候,reloadData 會導致很大的效能浪費,更慘的是tableview會有不必要的動畫被看到。
有一種解法是放delegate去給tableviewcell執行,這樣不用重繪tableview的整個cell,但還是會給所有的cell執行一次。
更好的作法是先更新tableview data,但不去reloadData,然後只更新看得見的部份
for (EmployeeListTableViewCell *cell in self.tableView.visibleCells) {
//TODO: update the UI
}
剩下的交給tableView自己,在滑動到其他cell的時候更新。
2018年12月14日 星期五
2018年11月6日 星期二
UIApplicationWillChangeStatusBarOrientationNotification v.s. UIApplicationDidChangeStatusBarOrientationNotification
結論:
UIApplicationWillChangeStatusBarOrientationNotification
會先被呼叫,而且userInfo 是傳送將要轉換的 orientation。
UIApplicationDidChangeStatusBarOrientationNotification
後被呼叫,但userInfo是傳送轉換前的 orientation。
code:
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(statusOrientationWillChange:)
name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(statusOrientationDidChange:)
name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
- (void)statusOrientationWillChange:(NSNotification *)notification {
if (notification.userInfo != nil && notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] != nil) {
UIInterfaceOrientation orientation = (UIInterfaceOrientation)[(NSNumber *)notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] integerValue];
if (UIInterfaceOrientationIsLandscape(orientation)) {
self.isLandscape = YES;
} else {
self.isLandscape = NO;
}
}
[self.tableView reloadData];
}
- (void)statusOrientationDidChange:(NSNotification *)notification {
if (notification.userInfo != nil && notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] != nil) {
UIInterfaceOrientation orientation = (UIInterfaceOrientation)[(NSNumber *)notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] integerValue];
if (UIInterfaceOrientationIsLandscape(orientation)) {
self.isLandscape = YES;
} else {
self.isLandscape = NO;
}
}
}
Strange thing: A - 2 > 0 is not equvalent to A > 2
(lldb) po self.customFields.count - 2 > 0
true
(lldb) po self.customFields.count > 2
false
(lldb) po self.customFields
<__NSArrayM 0x108e54540>(
)
(lldb) po self.customFields.count
<nil>
true
(lldb) po self.customFields.count > 2
false
(lldb) po self.customFields
<__NSArrayM 0x108e54540>(
)
(lldb) po self.customFields.count
<nil>
2018年11月2日 星期五
reuse custom tableview cell
當你用dynamic style cell的時候
所有關於cell的改變都要基於cell source
所以最好的方式就是直接create a Class for table view source
讓整個改動都變得有彈性
不要直接去改當下那個cell,那是不合邏輯的,而且你會修bug修不完
例如,你從coredate的一個表中拿到資料,不要直接拿來用
創一個class去包它,這個class可以自由加入你要的其他設定給你的custom table cell
所有關於cell的改變都要基於cell source
所以最好的方式就是直接create a Class for table view source
讓整個改動都變得有彈性
不要直接去改當下那個cell,那是不合邏輯的,而且你會修bug修不完
例如,你從coredate的一個表中拿到資料,不要直接拿來用
創一個class去包它,這個class可以自由加入你要的其他設定給你的custom table cell
2018年10月18日 星期四
提早拿到constraint後的frame
加入viewDidLoad可以拿到autolayout後的正確frame
加在animation中,可以在autolayout的情況下作用
[UIView animateWithDuration:0.5 animations:^{
self.filterViewWidthConstraint.constant = 200;
[self.view layoutIfNeeded];
}];
加在animation中,可以在autolayout的情況下作用
[UIView animateWithDuration:0.5 animations:^{
self.filterViewWidthConstraint.constant = 200;
[self.view layoutIfNeeded];
}];
用pdf取代png
在asset中放入pdf,設成all scale,可以用來取代svg當作向量圖。
如果是icon的話,還可以設成templete,可以用tintColor來改變icon的顏色。
超級實用的小技巧
如果是icon的話,還可以設成templete,可以用tintColor來改變icon的顏色。
超級實用的小技巧
2018年10月15日 星期一
Add touch began to pan gesture
!!!!!!一定要做#import "UIPanGestureRecognizerSubclass.h"
然後用category或是subclass去設定這個state,
才能用readwrite不然,原本的recognizer是readonly
@interface TouchableImageView:UIImageView
@property(strong, nonatomic) UIPanGestureRecognizer *panGR; // 這裡要改成自己的recognizer或是實作一個category
@end
@implementation TouchableImageView
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.panGR.state = UIGestureRecognizerStateBegan;
}
@end
//直接改狀態就能trigger gesture的began!!
然後用category或是subclass去設定這個state,
才能用readwrite不然,原本的recognizer是readonly
@interface TouchableImageView:UIImageView
@property(strong, nonatomic) UIPanGestureRecognizer *panGR; // 這裡要改成自己的recognizer或是實作一個category
@end
@implementation TouchableImageView
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.panGR.state = UIGestureRecognizerStateBegan;
}
@end
//直接改狀態就能trigger gesture的began!!
2018年9月24日 星期一
用自己的dependency專案到cocoapods中
我送了一個pull request 到starscream,增加了支援http proxy的功能,
但遲遲沒有人審核,這時只好在專案中使用用自己的github上的版本。
問題是startcream只是Socket.IO-clinet這個專案的dependent project,
而Podfile不支援更來url來使用你自己project來作為其他專案dependent project。
所以我必須要在podfile中取消dependency再把二個專案都加入pods
建好xcworkspace檔案之後,再手動修改pod.xcodeproj來連Socket.IO-clinet使用starscream來build
另一個方式是把starscream設為private pod,然後用podfile就可以取得。
但遲遲沒有人審核,這時只好在專案中使用用自己的github上的版本。
問題是startcream只是Socket.IO-clinet這個專案的dependent project,
而Podfile不支援更來url來使用你自己project來作為其他專案dependent project。
所以我必須要在podfile中取消dependency再把二個專案都加入pods
建好xcworkspace檔案之後,再手動修改pod.xcodeproj來連Socket.IO-clinet使用starscream來build
- 去Socket.IO-clinet的project
- 去Build Phases
- 在Target Dependencies 中加入starscream
- 在Link Binary With Libraries 加入 starscream
- 最重要的是在Build Settings中把Framework Search Paths中把starscream的位置"$PODS_CONFIGURATION_BUILD_DIR/Starscream"加進去 (記得要在上面選到All才看得到這個設定)
另一個方式是把starscream設為private pod,然後用podfile就可以取得。
2018年7月1日 星期日
predicateWithFormat在比對NSNumber的坑
環境: iOS SDK9
在你想比對NSNumber時,從JSON轉出來的數值出現了NSTaggedPointerString,你會比對不到,於是得到nil.
- (Visitor *)visitorByID:(NSNumber *)uid {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:@"Visitor" inManagedObjectContext:self.managedObjectContext]];
if ([uid isKindOfClass:[NSNumber class]]) {
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"self.visitor_id == %@", uid]];
} else {
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
NSNumber *numUid = [numberFormatter numberFromString:(NSString *)uid];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"self.visitor_id == %@", numUid]];
}
NSError * error = nil;
NSArray * result = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (error!=nil) {
SOLogError(@"%s[%d:%s]: %@", __FILE__, __LINE__, __FUNCTION__,error.localizedDescription);
}
}
解法:
把NSTaggedPointerString再轉回NSNumber一次
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
NSNumber *numUid = [numberFormatter numberFromString:(NSString *)uid];
在你想比對NSNumber時,從JSON轉出來的數值出現了NSTaggedPointerString,你會比對不到,於是得到nil.
- (Visitor *)visitorByID:(NSNumber *)uid {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:@"Visitor" inManagedObjectContext:self.managedObjectContext]];
if ([uid isKindOfClass:[NSNumber class]]) {
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"self.visitor_id == %@", uid]];
} else {
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
NSNumber *numUid = [numberFormatter numberFromString:(NSString *)uid];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"self.visitor_id == %@", numUid]];
}
NSError * error = nil;
NSArray * result = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (error!=nil) {
SOLogError(@"%s[%d:%s]: %@", __FILE__, __LINE__, __FUNCTION__,error.localizedDescription);
}
}
解法:
把NSTaggedPointerString再轉回NSNumber一次
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
NSNumber *numUid = [numberFormatter numberFromString:(NSString *)uid];
CoreData的performBlock和performBlockAndWait的差異
performBlock
會異步執行在context的thread之中(不會阻塞當前的thread)
performBlockAndWait
會同步執行在context的thread之中(會阻塞當前的thread,直到執行完block中的code再返回)
NSManagedObjectContext save()
https://developer.apple.com/documentation/coredata/nsmanagedobjectcontext/1506866-save
https://developer.apple.com/documentation/coredata/nsmanagedobjectcontext/1506954-haschanges
https://developer.apple.com/documentation/coredata/nsmanagedobjectcontext/1506954-haschanges
2018年6月28日 星期四
[array copy] 和 [NSArray arrayWithArray:array] 的異同
// https://segmentfault.com/q/1010000003854902
如果
如果
array
是immutable
类型的NSArray
copy
只是retain
,没有创建新对象arrayWithArray
创建了新的NSArray
对象,并将原有数组元素填充进去,数组元素还是原来的对象
array
是mutable
类型的NSArray
- 二者最终结果是等效的,创建了新的
NSArray
对象,但数据元素还是原来的对象
array == nil
copy
的结果是nil
arrayWithArray
结果是长度为0的NSArray
对象
2018年6月12日 星期二
nslog to file
- (void)redirectLogToDocuments {
NSArray *allPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [allPaths objectAtIndex:0];
NSString *pathForLog = [documentsDirectory stringByAppendingPathComponent:@"logFile.txt"];
if ([[NSFileManager defaultManager] fileExistsAtPath:pathForLog]) {
NSError *error;
[[NSFileManager defaultManager] removeItemAtPath:pathForLog error:&error];
if (error!=nil) {
NSLog(@"%@", error.localizedDescription);
}
}
freopen([pathForLog cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr);
}
NSArray *allPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [allPaths objectAtIndex:0];
NSString *pathForLog = [documentsDirectory stringByAppendingPathComponent:@"logFile.txt"];
if ([[NSFileManager defaultManager] fileExistsAtPath:pathForLog]) {
NSError *error;
[[NSFileManager defaultManager] removeItemAtPath:pathForLog error:&error];
if (error!=nil) {
NSLog(@"%@", error.localizedDescription);
}
}
freopen([pathForLog cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr);
}
讓圖示在AttributedText上下置中
https://stackoverflow.com/questions/26105803/center-nstextattachment-image-next-to-single-line-uilabel
let iconImage = UIImage(named: "icon.png")!
var icon = NSTextAttachment()
icon.bounds = CGRect(x: 0, y: (titleFont.capHeight - iconImage.size.height).rounded() / 2, width: iconImage.size.width, height: iconImage.size.height)
icon.image = iconImage
let iconString = NSAttributedString(attachment: icon)
titleText.append(iconString)
2018年6月8日 星期五
rate the app
- (IBAction)rateButtonTapped:(UIButton *)sender {
if (@available(iOS 10.3, *)) {
[SKStoreReviewController requestReview];
} else {
NSURL *url = [NSURL URLWithString:@"itms-apps://itunes.apple.com/tw/app/swipedon-visitor-management/id986185962?l=en&mt=8"];
[[UIApplication sharedApplication] openURL:url];
}
}
if (@available(iOS 10.3, *)) {
[SKStoreReviewController requestReview];
} else {
NSURL *url = [NSURL URLWithString:@"itms-apps://itunes.apple.com/tw/app/swipedon-visitor-management/id986185962?l=en&mt=8"];
[[UIApplication sharedApplication] openURL:url];
}
}
2018年6月5日 星期二
用CoreData存圖片
不要用core data存圖
是可以轉存NSData去存,但只要你的ManagedObjectContext沒有釋放,那麼那張圖就不會被釋放,如果圖片大一點,一下就爆了
用core data存imageName,把圖片存到文件資料夾裡,用imageName去對應,
並且監聽imageName的setter,在改名之前先刪去原來的圖。
是可以轉存NSData去存,但只要你的ManagedObjectContext沒有釋放,那麼那張圖就不會被釋放,如果圖片大一點,一下就爆了
用core data存imageName,把圖片存到文件資料夾裡,用imageName去對應,
並且監聽imageName的setter,在改名之前先刪去原來的圖。
2018年5月29日 星期二
block中的self
Medium上有一篇文章說用_variable取代self.variable 就不會循環引用
實驗之後發現:
_variable也會
還是宣告成weak最好
既使weakSelf去呼叫一個方法,方法裡面有不是weak的self,那還是不會循環引用的。
用變數型態直接宣告在.h或.m,就算加了__weak還是一樣會循環引用,得要改成property的型態並宣告成weak才能避免
如果你在block中使用一些singleton例如[UIApplication sharedApplication],只要不要用變數去接它,就可以安全下莊
實驗之後發現:
_variable也會
還是宣告成weak最好
既使weakSelf去呼叫一個方法,方法裡面有不是weak的self,那還是不會循環引用的。
用變數型態直接宣告在.h或.m,就算加了__weak還是一樣會循環引用,得要改成property的型態並宣告成weak才能避免
如果你在block中使用一些singleton例如[UIApplication sharedApplication],只要不要用變數去接它,就可以安全下莊
2018年5月25日 星期五
WKWebView
// Inject新的attribute給你的wkwebview
- (void)setupWebView {
NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('initial-scale', '1'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
WKUserScript *userScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
[userContentController addUserScript:userScript];
WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init];
wkWebConfig.userContentController = userContentController;
// 因為pdf的高度用webview的寬度去調整,所以要先設定好。
CGRect initialWebViewFrame = self.view.frame;
if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortrait ||
[[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortraitUpsideDown) {
CGFloat height = initialWebViewFrame.size.height;
CGFloat width = initialWebViewFrame.size.width;
if (height > width) {
initialWebViewFrame.size = CGSizeMake(width, 0);
} else {
initialWebViewFrame.size = CGSizeMake(height, 0);
}
}
_webView = [[WKWebView alloc] initWithFrame:initialWebViewFrame configuration:wkWebConfig];
_webView.scrollView.contentSize = initialWebViewFrame.size;
[_webViewContainer addSubview:_webView];
_webView.navigationDelegate = self;
[_webViewContainer addConstraint:[NSLayoutConstraint constraintWithItem:_webView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_webViewContainer attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]];
[_webViewContainer addConstraint:[NSLayoutConstraint constraintWithItem:_webView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:_webViewContainer attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0]];
[_webViewContainer addConstraint:[NSLayoutConstraint constraintWithItem:_webView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:_webViewContainer attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]];
[_webViewContainer addConstraint:[NSLayoutConstraint constraintWithItem:_webView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:_webViewContainer attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0]];
_webView.scrollView.scrollEnabled = NO;
[_webView.scrollView setBackgroundColor:[UIColor clearColor]];
[_webView setBackgroundColor:[UIColor clearColor]];
[_webView setOpaque:NO];
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
if ([[SettingsManager sharedManager].agreement[@"agreement_type"] isEqualToString:@"pdf"]) {
} else {
if (shouldReloadFlag) {
shouldReloadFlag = NO;
[self loadWebView];
}
}
[self resizeScrollView];
}
- (void)resizeScrollView {
if ([[SettingsManager sharedManager].agreement[@"agreement_type"] isEqualToString:@"pdf"]) {
if (_webView.isLoading == false) {
for (UIView *subview in self.webView.scrollView.subviews) {
subview.backgroundColor = [UIColor clearColor];
// 取得pdf的高
if ([[[subview class] description] isEqualToString:@"WKPDFView"]) {
NSLog(@"pdfview.size: %@ webviewContainer.size:%@", NSStringFromCGSize(subview.frame.size), NSStringFromCGSize(_webViewContainer.frame.size));
[self resizeScrollViewByWebViewContentHeight:subview.frame.size.height / subview.frame.size.width * _webViewContainer.frame.size.width];
break;
}
}
}
} else {
if (_webView.isLoading == false) {
__weak typeof(self) weakSelf = self;
[_webView evaluateJavaScript:@"document.body.offsetHeight" completionHandler:^(id result, NSError * _Nullable error) {
CGFloat height = [result floatValue] + 30.0;
[weakSelf resizeScrollViewByWebViewContentHeight:height];
}];
}
}
}
- (void)setupWebView {
NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('initial-scale', '1'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
WKUserScript *userScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
[userContentController addUserScript:userScript];
WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init];
wkWebConfig.userContentController = userContentController;
// 因為pdf的高度用webview的寬度去調整,所以要先設定好。
CGRect initialWebViewFrame = self.view.frame;
if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortrait ||
[[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortraitUpsideDown) {
CGFloat height = initialWebViewFrame.size.height;
CGFloat width = initialWebViewFrame.size.width;
if (height > width) {
initialWebViewFrame.size = CGSizeMake(width, 0);
} else {
initialWebViewFrame.size = CGSizeMake(height, 0);
}
}
_webView = [[WKWebView alloc] initWithFrame:initialWebViewFrame configuration:wkWebConfig];
_webView.scrollView.contentSize = initialWebViewFrame.size;
[_webViewContainer addSubview:_webView];
_webView.navigationDelegate = self;
[_webViewContainer addConstraint:[NSLayoutConstraint constraintWithItem:_webView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_webViewContainer attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]];
[_webViewContainer addConstraint:[NSLayoutConstraint constraintWithItem:_webView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:_webViewContainer attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0]];
[_webViewContainer addConstraint:[NSLayoutConstraint constraintWithItem:_webView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:_webViewContainer attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]];
[_webViewContainer addConstraint:[NSLayoutConstraint constraintWithItem:_webView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:_webViewContainer attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0]];
_webView.scrollView.scrollEnabled = NO;
[_webView.scrollView setBackgroundColor:[UIColor clearColor]];
[_webView setBackgroundColor:[UIColor clearColor]];
[_webView setOpaque:NO];
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
if ([[SettingsManager sharedManager].agreement[@"agreement_type"] isEqualToString:@"pdf"]) {
} else {
if (shouldReloadFlag) {
shouldReloadFlag = NO;
[self loadWebView];
}
}
[self resizeScrollView];
}
- (void)resizeScrollView {
if ([[SettingsManager sharedManager].agreement[@"agreement_type"] isEqualToString:@"pdf"]) {
if (_webView.isLoading == false) {
for (UIView *subview in self.webView.scrollView.subviews) {
subview.backgroundColor = [UIColor clearColor];
// 取得pdf的高
if ([[[subview class] description] isEqualToString:@"WKPDFView"]) {
NSLog(@"pdfview.size: %@ webviewContainer.size:%@", NSStringFromCGSize(subview.frame.size), NSStringFromCGSize(_webViewContainer.frame.size));
[self resizeScrollViewByWebViewContentHeight:subview.frame.size.height / subview.frame.size.width * _webViewContainer.frame.size.width];
break;
}
}
}
} else {
if (_webView.isLoading == false) {
__weak typeof(self) weakSelf = self;
[_webView evaluateJavaScript:@"document.body.offsetHeight" completionHandler:^(id result, NSError * _Nullable error) {
CGFloat height = [result floatValue] + 30.0;
[weakSelf resizeScrollViewByWebViewContentHeight:height];
}];
}
}
}
2018年5月19日 星期六
keyboard disappaer when tableView scrolling
2018年4月25日 星期三
獲取UIWebView的內容高度
// 直接抓
CGSize newSize = [webView.scrollView contentSize];
webViewHeightConstraint.constant = newSize.height;
// 如果是單純本地端的html,這樣比較準
CGFloat webViewHeight = [[webView stringByEvaluatingJavaScriptFromString:@"document.body.offsetHeight"] floatValue];
textViewHeightConstraint.constant = webViewHeight;
CGSize newSize = [webView.scrollView contentSize];
webViewHeightConstraint.constant = newSize.height;
// 如果是單純本地端的html,這樣比較準
CGFloat webViewHeight = [[webView stringByEvaluatingJavaScriptFromString:@"document.body.offsetHeight"] floatValue];
textViewHeightConstraint.constant = webViewHeight;
2018年4月18日 星期三
設定用UIModalPresentationFormSheet推出之後的視窗尺寸
categoriesViewController.preferredContentSize = CGSizeMake(440.0, 320.0);
categoriesViewController.modalPresentationStyle = UIModalPresentationFormSheet;
categoriesViewController.modalPresentationStyle = UIModalPresentationFormSheet;
2018年4月13日 星期五
get mode name
#import <sys/utsname.h>
struct utsname systemInfo;
uname(&systemInfo);
NSString *currentDeviceModel = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
struct utsname systemInfo;
uname(&systemInfo);
NSString *currentDeviceModel = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
2018年3月27日 星期二
存取層級(swift3.0以上適用)
- open:可以在module外面被使用,也可以被override。
- public:可以在module外面被使用,不可以被override。
- internal:不可以在module外面被使用。
- fileprivate:只可以在同一個file中所定義的class間使用。
- private:只可以在宣告它的class中使用,不被其他class存取。
2018年2月28日 星期三
flip image horizontally 左右翻轉圖片
UIImage *flippedImage = [UIImage imageWithCGImage: yourImage.CGImage
scale:trimmedImage.scale
orientation:UIImageOrientationUpMirrored];
scale:trimmedImage.scale
orientation:UIImageOrientationUpMirrored];
orientation 螢幕方向
// 這個是裝置的實際方向
[UIDevice currentDevice].orientation
// 這個是螢幕的方向(如果你鎖住一些方向,他就不會顯示那些方向)
[UIApplication sharedApplication].statusBarOrientation
// 旋轉螢幕時的callback
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(deviceRotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
func deviceRotated(){
if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) {
print("Landscape")
// Resize other things
}
if UIDeviceOrientationIsPortrait(UIDevice.current.orientation) {
print("Portrait")
// Resize other things
}
}
[UIDevice currentDevice].orientation
// 這個是螢幕的方向(如果你鎖住一些方向,他就不會顯示那些方向)
[UIApplication sharedApplication].statusBarOrientation
// 旋轉螢幕時的callback
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(deviceRotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
func deviceRotated(){
if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) {
print("Landscape")
// Resize other things
}
if UIDeviceOrientationIsPortrait(UIDevice.current.orientation) {
print("Portrait")
// Resize other things
}
}
2018年2月20日 星期二
nested preprocessor ifdef
#ifdef PRODUCTION
[buildInfoLabel setText:@""];
#else
#ifdef App_Sentry_Environment
[buildInfoLabel setText:App_Sentry_Environment];
#endif
#endif
[buildInfoLabel setText:@""];
#else
#ifdef App_Sentry_Environment
[buildInfoLabel setText:App_Sentry_Environment];
#endif
#endif
2018年2月19日 星期一
Compiler Warning messages
no rule to process file of type net.daringfireball.markdown for architecture x86_64
- Select Project Navigator
- Select your project
- Select your target
- Select Build Phases
- Move files (in this case, CHANGLELOG.md) that you don't want the compiler to process from
Compile Sources
toCopy Bundle Resources
Property access result unused - getters should not be used for side effects
self.lables.removeLastObject; 方法不要用. 要用[],不然compiler會認為是getter
[self.lables removeLastObject];
Implicit conversion from enumeration type 'SO_LOG_LEVEL' to different enumeration type 'DDLogFlag' (aka 'enum DDLogFlag')
LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, level, 0, nil, __PRETTY_FUNCTION__, message); 要先轉好型再放進去
LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, (DDLogFlag)level, 0, nil, __PRETTY_FUNCTION__, message);
Format string is not a string literal (potentially insecure)
LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, (DDLogFlag)level, 0, nil, __PRETTY_FUNCTION__, message); 參數是是用Format String不是普通String
LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, (DDLogFlag)level, 0, nil, __PRETTY_FUNCTION__, @"%@", message);
PerformSelector may cause a leak because its selector is unknown
因為可能會找不到function所以使用selector可能會有leak,這裡我們直接忽略它
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [self performSelector: aSelector]; #pragma clang diagnostic pop
warning: Consistency Error: Setting the No Action Delete Rule on EmployeeQueue.child may result in relationships to deleted objects
Fixed width constraints may cause clipping
這是Localization Issue,Xcode9擔心我們的文字輸入框在固定寛度的情況下,文字過長時會被clip掉,你可以用它推薦的解法,把trailing或leading設為>=或<=現在的寬度,也可以移除這個constrains
如果是textField你不會想在使用者輸入太長的時候延長它,你可以把width改成依container的寬度比例來work around.
2018年2月16日 星期五
版本號判斷
Starting Xcode 9, in Objective-C:
if (@available(iOS 11, *)) {
// iOS 11 (or newer) ObjC code
} else {
// iOS 10 or older code
}
Starting Xcode 7, in Swift: if #available(iOS 11, *) {
// iOS 11 (or newer) Swift code
} else {
// iOS 10 or older code
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 // iOS 11
#else
#endif
如果你是用10.3的SDK,那你就會看到下面的設定 (
在10.3SDK中的AvailabilityInternal.h
)
/* make sure a default max version is set */
#ifndef __IPHONE_OS_VERSION_MAX_ALLOWED
#define __IPHONE_OS_VERSION_MAX_ALLOWED __IPHONE_10_3
#endif
#define __IPHONE_2_0 20000
#define __IPHONE_2_1 20100
#define __IPHONE_2_2 20200
#define __IPHONE_3_0 30000
#define __IPHONE_3_1 30100
#define __IPHONE_3_2 30200
#define __IPHONE_4_0 40000
#define __IPHONE_4_1 40100
#define __IPHONE_4_2 40200
#define __IPHONE_4_3 40300
#define __IPHONE_5_0 50000
#define __IPHONE_5_1 50100
#define __IPHONE_6_0 60000
#define __IPHONE_6_1 60100
#define __IPHONE_7_0 70000
#define __IPHONE_7_1 70100
#define __IPHONE_8_0 80000
#define __IPHONE_8_1 80100
#define __IPHONE_8_2 80200
#define __IPHONE_8_3 80300
#define __IPHONE_8_4 80400
#define __IPHONE_9_0 90000
#define __IPHONE_9_1 90100
#define __IPHONE_9_2 90200
#define __IPHONE_9_3 90300
#define __IPHONE_10_0 100000
#define __IPHONE_10_1 100100
#define __IPHONE_10_2 100200
#define __IPHONE_10_3 100300
/* __IPHONE_NA is not defined to a value but is uses as a token by macros to indicate that the API is unavailable */
2018年1月17日 星期三
模仿拍完照的閃現效果
self.view.alpha = 0.0;
[UIView animateWithDuration: 0.5
animations: ^{
self.view.alpha = 1.0;
}
completion: ^(BOOL finished) {
}
];
[UIView animateWithDuration: 0.5
animations: ^{
self.view.alpha = 1.0;
}
completion: ^(BOOL finished) {
}
];
2018年1月12日 星期五
在moveRowAtIndexPath動畫之後加入callback
[CATransaction begin];
[CATransaction setCompletionBlock: ^{
// Code to be executed upon completion
[self connectPrinterWithDeviceInfo:deviceInfo];
}];
[tableView beginUpdates];
[tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
[tableView endUpdates];
[CATransaction commit];
[CATransaction setCompletionBlock: ^{
// Code to be executed upon completion
[self connectPrinterWithDeviceInfo:deviceInfo];
}];
[tableView beginUpdates];
[tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
[tableView endUpdates];
[CATransaction commit];
2018年1月9日 星期二
在段落中某些特定文字加入連結
- 只有UITextView可以做到。
- 要實作UITextViewDelegate
#pragma mark - UITextViewDelegate
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(nonnull NSURL *)URL inRange:(NSRange)characterRange {
//可以做些判斷
return YES;
}
- 加入某段文字
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:str];
[attrStr addAttribute:NSLinkAttributeName value:[NSURL URLWithString:@"https://yourUrl.com"] range:[str rangeOfString:@"Knowledge base"]];
self.linkTextView.attributedText = attrStr;
- 整段文字
NSAttributedString *attrStr = [[NSAttributedString alloc]initWithString:@"please visit our Knowledge base" attributes:dictAttr];
self.linkTextView.attributedText = attrStr;
2018年1月3日 星期三
去特定的setting頁
// wifi settings
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"
App-Prefs
:root=WIFI"]];
// Bluetooth settings
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"
App-Prefs
:root=
Bluetooth
"]];
訂閱:
文章 (Atom)