iOS零碎知识点<初级版>

iOS零碎知识点<初级版>
iOS零碎知识点<中阶版>
iOS零碎知识点<高阶版>
iOS零碎知识点<工具篇>


优雅的隐藏tabbar

很多APP都使用TabBarController套NavigationController的方法来作为应用的框架,那么隐藏TabBar就成了一个必要的功能,目前最简单的方法还是使用hidesBottomBarWhenPushed来实现,最简单的方法就是在要隐藏tab bar的Controller里写入下面的方法(可以写个类扩展来更方便)来覆默认值:

- (BOOL)hidesBottomBarWhenPushed {
    return (self.navigationController.topViewController == self);
}


把UITableview在editing模式下的drag按钮去掉,换成自己的样式,并保留原生拖动排序的行为

先找到了UITableViewCell的结构,并将拖动按钮替换:

//打印出来的自定义的cell在editing模式下的结构

(lldb) po self
>
(lldb) po self.subviews
5 elements
- [0] : ; layer = >
- [1] : <_UITableViewCellSeparatorView: 0x7d087c40; frame = (15 55; 305 1); layer = >
- [2] : <_UITableViewCellSeparatorView: 0x7b163240; frame = (15 55.5; 305 0.5); layer = >
- [3] : >
- [4] : >
(lldb) po self.subviews.last
Optional
- Some : >
(lldb) po self.subviews.last?.subviews
Optional>
Some : 1 elements
- [0] : >
(lldb)

可以看到此时contentView左右都向内缩进了一定的距离,最后有一个view叫UITableViewCellReorderControl,就是它了,然后看它的subviews,竟然包含了一个UIImageView,果断替换之,代码如下:

 override func layoutSubviews() {
        super.layoutSubviews()
        setupReorderControl()
    }
    
    func setupReorderControl() {
        if (self.reorderControl != nil) {
            return;
        }
        
        for view in self.subviews {
            if view.description.containsString("UITableViewCellReorderControl") {
                self.reorderControl = view
            }
        }
        
        if ((self.reorderControl) != nil)
        {
            let imageOfReorder = self.reorderControl?.subviews[0] as? UIImageView
            imageOfReorder?.removeFromSuperview()
        }
    }

此时就完成了将拖动按钮隐藏的功能,但是注意,在这里如果想通过设置reorderControl的frame去改变它的位置是不成功的,我想可能它的布局使用autolayout,并没有深入的再去研究。


改變導航欄返回按鈕,iOS11上失效:

[[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonImage
                                                 forState:UIControlStateNormal
                                               barMetrics:UIBarMetricsDefault];

//自定义文字部分
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(NSIntegerMin, NSIntegerMin)  forBarMetrics:UIBarMetricsDefault];


判定滚动手势是往上还是往下拖拽

//scrollView已经有拖拽手势,直接拿到scrollView的拖拽手势
    UIPanGestureRecognizer *pan = scrollView.panGestureRecognizer;
    CGFloat velocity = [pan velocityInView:scrollView].y;
    CGFloat offsetY = _lastPointY - scrollView.contentOffset.y;
    
    if (velocity < -5) {
        //向上拖动,隐藏导航栏
       
    } else if (velocity > 5) {
        //向下拖动,显示导航栏
       
    } else if(velocity == 0){
        //停止拖拽
    }


ios UITableview header不随着滚动

//去掉UItableview headerview黏性(sticky)
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat sectionHeaderHeight = 40;
    if (scrollView.contentOffset.y<=sectionHeaderHeight&&scrollView.contentOffset.y>=0) {
        scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);
    } else if (scrollView.contentOffset.y>=sectionHeaderHeight) {
        scrollView.contentInset = UIEdgeInsetsMake(-sectionHeaderHeight, 0, 0, 0);
    }
} 


UITableView的子视图会自动往上偏移64

大概结构是这样的:tableview上添加了个subview,然后设置了tableview的内偏移为subview的高,还设置了tableview的tableFooterView;运行后发现tableview的contentoffset自动往上多偏移了64;

self.automaticallyAdjustsScrollViewInsets = NO;
    centerTableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight - kNavgationHeight) style:UITableViewStyleGrouped];
    centerTableView.showsVerticalScrollIndicator = NO;
    centerTableView.tableFooterView = self.bottomView;
    [centerTableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
    centerTableView.contentInset = UIEdgeInsetsMake(_centerViewHeight, 0, 0, 0);
    [self.view addSubview:centerTableView];

    _userHeadView = [BKUserCenterView loadViewFromNib];
    _userHeadView.frame = CGRectMake(0, -_centerViewHeight, kScreenWidth, _centerViewHeight);
    [centerTableView addSubview:_userHeadView];

导致原因:先设置了tableFooterView,再addSubview: 导致tableview的计算就会不准确,把设置tableFooterView 放到 addSubview: 后面就好了,更改后如下:

.......上面其它代码不变......
[centerTableView addSubview:_userHeadView];
centerTableView.tableFooterView = self.bottomView;


获取相片的位置等信息:

NSURL *assetURL = [info objectForKey:UIImagePickerControllerReferenceURL];
        ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
        __block NSMutableDictionary *imageMetadata_GPS = nil;

        __weak typeof(self)weakSelf = self;
        [library assetForURL:assetURL resultBlock:^(ALAsset *asset) {

             //获取时间
             NSDate* pictureDate = [asset valueForProperty:ALAssetPropertyDate];
             NSDateFormatter * formatter = [[NSDateFormatter alloc]init];
             formatter.dateFormat = @"yyyy:MM:dd HH:mm:ss";
             formatter.timeZone = [NSTimeZone localTimeZone];
             NSString * pictureTime = [formatter stringFromDate:pictureDate];
             weakSelf.time.text = pictureTime;

             //获取GPS
             imageMetadata_GPS = [[NSMutableDictionary alloc] initWithDictionary:asset.defaultRepresentation.metadata];

             NSDictionary *GPSDict=[imageMetadata_GPS objectForKey:(NSString*)kCGImagePropertyGPSDictionary];

             if (GPSDict!=nil) {

                 CLLocation *loc=[GPSDict locationFromGPSDictionary];

                 weakSelf.weidu.text = [NSString stringWithFormat:@"%f", loc.coordinate.latitude];
                 weakSelf.jingdu.text = [NSString stringWithFormat:@"%f", loc.coordinate.longitude];

                 CLGeocoder *clGeoCoder = [[CLGeocoder alloc] init];
                 CLLocation *newLocation = [[CLLocation alloc] initWithLatitude:loc.coordinate.latitude longitude:loc.coordinate.longitude];

                 //反向地理编码的请求 -> 根据经纬度 获取 位置
                 [clGeoCoder reverseGeocodeLocation:newLocation completionHandler: ^(NSArray *placemarks,NSError *error) {
                     for (CLPlacemark *placeMark in placemarks)
                     {
                         NSDictionary *addressDic=placeMark.addressDictionary;
                         NSArray *location_Arr = [addressDic objectForKey:@"FormattedAddressLines"];//系统格式化后的位置
                         weakSelf.location.text = [location_Arr firstObject];
                     }
                 }];
             }else{
                      //@"此照片没有GPS信息";
                      //@"此照片没有GPS信息";
                      //@"此照片没有拍摄位置";
                 }
             }
            failureBlock:^(NSError *error) {
        }];
    }


获取网络图片的宽高

+(CGSize)getImageSizeWithURL:(id)imageURL
{
    NSURL * url = nil;
    if ([imageURL isKindOfClass:[NSURL class]]) {
        url = imageURL;
    }
    if ([imageURL isKindOfClass:[NSString class]]) {
        url = [NSURL URLWithString:imageURL];
    }
    if (!url) {
        return CGSizeZero;
    }
    CGImageSourceRef imageSourceRef =     CGImageSourceCreateWithURL((CFURLRef)url, NULL);
    CGFloat width = 0, height = 0;
    if (imageSourceRef) {
        CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSourceRef, 0, NULL);
        if (imageProperties != NULL) {
            CFNumberRef widthNumberRef = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelWidth);
            if (widthNumberRef != NULL) {
                CFNumberGetValue(widthNumberRef, kCFNumberFloat64Type, &width);
            }
            CFNumberRef heightNumberRef = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelHeight);
            if (heightNumberRef != NULL) {
                CFNumberGetValue(heightNumberRef, kCFNumberFloat64Type, &height);
            }
            CFRelease(imageProperties);
        }
        CFRelease(imageSourceRef);
    }
    return CGSizeMake(width, height);
}


tableView下拉导航栏上头像变大:

UIView *titleView = [[UIView alloc] init];
  self.navigationItem.titleView = titleView;

  self.headerImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"head.jpg"]];
  self.headerImageView.layer.cornerRadius = 35;
  self.headerImageView.layer.masksToBounds = YES;
  self.headerImageView.frame = CGRectMake(0, 0, 70, 70);
  self.headerImageView.center = CGPointMake(titleView.center.x, 0);
  [titleView addSubview:self.headerImageView];
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
  CGFloat offsetY = scrollView.contentOffset.y + scrollView.contentInset.top;
  
  CGFloat scale = 1.0;
  // 放大
  if (offsetY < 0) {
    // 允许下拉放大的最大距离为300
    // 1.5是放大的最大倍数,当达到最大时,大小为:1.5 * 70 = 105
    // 这个值可以自由调整
    scale = MIN(1.5, 1 - offsetY / 300);
  } else if (offsetY > 0) { // 缩小
    // 允许向上超过导航条缩小的最大距离为300
    // 为了防止缩小过度,给一个最小值为0.45,其中0.45 = 31.5 / 70.0,表示
    // 头像最小是31.5像素
    scale = MAX(0.45, 1 - offsetY / 300);
  }
  
  self.headerImageView.transform = CGAffineTransformMakeScale(scale, scale);

  // 保证缩放后y坐标不变
  CGRect frame = self.headerImageView.frame;
  frame.origin.y = -self.headerImageView.layer.cornerRadius / 2;
  self.headerImageView.frame = frame;
}


画一个渐变色的圆

- (void)drawRect:(CGRect)rect {

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    CGContextAddArc(ctx, self.centerX, self.centerY, self.height / 2, 0, M_PI * 2, 0);
    
    CGContextSetLineWidth(ctx, 8);
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    //    颜色数组
    NSArray *colors = @[
                        (id)[UIColor colorWithRed:86 / 255.f green:216 / 255.f blue:252 / 255.f alpha:1].CGColor,
                        (id)[UIColor colorWithRed:248 / 255.f green:114 / 255.f blue:155 / 255.f alpha:1].CGColor
                        ];
    
    CGGradientRef gradientRef = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, NULL);
    
    CGColorSpaceRelease(colorSpace);
    CGContextReplacePathWithStrokedPath(ctx);
    CGContextClip(ctx);
    
    CGPoint beginPoint = CGPointMake(0, 0);
    CGPoint endPoint = CGPointMake(rect.size.width, rect.size.height);
    CGContextDrawLinearGradient(ctx, gradientRef, beginPoint, endPoint, 0);
    
    CGGradientRelease(gradientRef);
    
    CGContextStrokePath(ctx);

}


去除掉首尾的空白字符和换行字符

 [address stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 
//替换\n
  [address stringByReplacingOccurrencesOfString:@"\t" withString:@""];


ios打包ipa的四种实用方法(.app转.ipa)


基本数据类型的取值范围:

unsigned   int   0~4294967295   
int   -2147483648~2147483647 
unsigned long 0~4294967295
long   -2147483648~2147483647
long long的最大值:9223372036854775807
long long的最小值:-9223372036854775808
unsigned long long的最大值:1844674407370955161
__int64的最大值:9223372036854775807
__int64的最小值:-9223372036854775808
unsigned __int64的最大值:18446744073709551615


修改 UIAlertController、UIAlertAction文字颜色

UIAlertController * alertContro = [[UIAlertController alloc] init];
       NSMutableAttributedString *hogan = [[NSMutableAttributedString alloc] initWithString:@"heihei"];
        [hogan addAttribute:NSFontAttributeName value:kMediumFontIsB4IOS9(16) range:NSMakeRange(0, [[hogan string] length])];
        [hogan addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, [[hogan string] length])];
        [alertContro setValue:hogan forKey:@"attributedTitle"];

// UIAlertAction
        alertAction setValue:kRedColor forKey:@"_titleTextColor"];

Mac OS 10.12.3如何添加永久静态路由(亲测无效,不知道是我那里执行错了!)

https://discussionschinese.apple.com/thread/102393?start=0&tstart=0


判定是否有导入了某个头件

#if __has_include()
#import 
#else
#import "zzzzz.h"
#endif


动态库与静态库

区別:
静态库:链接时会被完整的复制到可执行文件中,被多次使用就有多份拷贝。
动态库:链接时不复制,程序运行时由系统动态加载到内存,系统只加载一次,多个程序共用(如系统的UIKit.framework等),节省内存。

  1. 如果静态库中有category类,则在使用静态库的项目配置中Other Linker Flags需要添加参数-ObjC或者-all_load。
    如果创建的framework类中使用了.tbd,则需要在实际项目中导入.tbd动态库。

  2. Mach-O格式,因为动态库也可以是以framework形式存在,所以需要设置,否则默认打出来的是动态库。将target->BuildSetting->Mach-o Type 设为Static Library(默认为Dynamic Library)

  3. 架構:
    模拟器:
    iPhone4s-iPnone5:i386
    iPhone5s-iPhone7 Plus:x86_64
    真机:
    iPhone3gs-iPhone4s:armv7
    iPhone5-iPhone5c:armv7s
    iPhone5s-iPhone7 Plus:arm64
    支持armv7的静态库可以在armv7s上正常运行。

手机号判定:

  • 方法一:建议使用这个
    if (self.length != 11)
    {
        return NO;
    }
    /**
     * 手机号码:
     * 13[0-9], 14[5,7], 15[0, 1, 2, 3, 5, 6, 7, 8, 9], 17[0, 1, 6, 7, 8], 18[0-9]
     * 移动号段: 134,135,136,137,138,139,147,150,151,152,157,158,159,170,178,182,183,184,187,188
     * 联通号段: 130,131,132,145,155,156,170,171,175,176,185,186
     * 电信号段: 133,149,153,170,173,177,180,181,189
     */
    NSString *MOBILE = @"^1(3[0-9]|4[57]|5[0-35-9]|7[0135678]|8[0-9])\\d{8}$";
    /**
     * 中国移动:China Mobile
     * 134,135,136,137,138,139,147,150,151,152,157,158,159,170,178,182,183,184,187,188
     */
    NSString *CM = @"^1(3[4-9]|4[7]|5[0-27-9]|7[08]|8[2-478])\\d{8}$";
    /**
     * 中国联通:China Unicom
     * 130,131,132,145,155,156,170,171,175,176,185,186
     */
    NSString *CU = @"^1(3[0-2]|4[5]|5[56]|7[0156]|8[56])\\d{8}$";
    /**
     * 中国电信:China Telecom
     * 133,149,153,170,173,177,180,181,189
     */
    NSString *CT = @"^1(3[3]|4[9]|53|7[037]|8[019])\\d{8}$";
    
    NSPredicate *regextestmobile = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", MOBILE];
    NSPredicate *regextestcm = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CM];
    NSPredicate *regextestcu = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CU];
    NSPredicate *regextestct = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", CT];
    
    if (([regextestmobile evaluateWithObject:self] == YES)
        || ([regextestcm evaluateWithObject:self] == YES)
        || ([regextestct evaluateWithObject:self] == YES)
        || ([regextestcu evaluateWithObject:self] == YES))
    {
        return YES;
    }
    else
    {
        return NO;
    }
}
  • 方法二:
- (BOOL)isMobile{
    NSString *regexStr = @"^1[3,8]\\d{9}|14[5,7,9]\\d{8}|15[^4]\\d{8}|17[^2,4,9]\\d{8}$";
    NSError *error;
    NSRegularExpression *regular = [NSRegularExpression regularExpressionWithPattern:regexStr options:NSRegularExpressionCaseInsensitive error:&error];
    if (error) return NO;
    NSInteger count = [regular numberOfMatchesInString:self options:NSMatchingReportCompletion range:NSMakeRange(0, self.length)];
    if (count > 0) {
        return YES;
    } else {
        return NO;
    }
}


适配ios11 @available属性oc工程无法在xcode9以下工程编译问题解决方案:

# ifdef __IPHONE_11_0
    if (@available(iOS 11.0, *)) {
        self.leftContentView.contenView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    } else {
        self.automaticallyAdjustsScrollViewInsets = NO;
    }
#else
    self.automaticallyAdjustsScrollViewInsets = NO;
#endif


获取WKWebview的内容高度

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    [webView evaluateJavaScript:@"document.body.offsetHeight" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
       CGFloat documentHeight = [result doubleValue];
        wkWebViewHeight = documentHeight;
        CGRect webFrame = webView.frame;
        webFrame.size.height = wkWebViewHeight;
        webView.frame = webFrame;
        [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:3 inSection:0], nil] withRowAnimation:UITableViewRowAnimationNone];
    }];
}


判定是否可以导入某个头文件:

//判定是否可以导入某头文件
#if __has_include()
  //do sth
#import 
//判定是否可以导入某framework库的头文件
#elif __has_include()
  //do sth
#else
  //do sth
#endif


查看.a库是否支持bitcode:

1. 首先需要判断 library 是否是 fat 的,可以用 lipo 命令:
  lipo -info ibDHxls.a

2. 如果是 fat library(支持多构架),需要将某个 CPU 架构的 slice 提取出来:
  lipo -thin arm64  ibDHxls.a -output libd-arm64.a

3. 接下来我们需要将这个 slice 里面的目标文件解压出来,可以用 ar 命令:
  ar -x libd-arm64.a

4. 解压完后当前的目录下会有多个.o文件,你选择其中一个.o文件执行下面的命令就可以:
  otool -l unit.o | grep bitcode //我这里选择的是:`unit.o`

5. 如果找到了,说明第三方库是支持 bitcode 的:
  `sectname __bitcode`


禁止WKWebView缩放:

  1. 在HTML里边的mata标签加入user-scalable = no,若是原生js交互的话可采用注入js的方法更改meta。
- (void)webView:(WKWebView*)webView didFinishNavigation:(WKNavigation*)navigation
{
    NSString *injectionJSString = @"var script = document.createElement('meta');"
    "script.name = 'viewport';"
    "script.content=\"width=device-width, initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0, user-scalable=no\";"
    "document.getElementsByTagName('head')[0].appendChild(script);";
    [webView evaluateJavaScript:injectionJSString completionHandler:nil];

}
  1. 第一种方法的缺点:你无法再手动控制缩放比例了,并且在双击、或者遇到文本输入的时候,可能还是会自动缩放,下面使用第二种方法完美控制(ios11之前,ios之后失效):
  • 设置代理
self.wk_WebView.scrollView.delegate = self;
  • webview的控制类中,设立一个控制属性,并初始化设置为YES:
self.allowZoom = YES;
  • 实现scrollView的代理:
// wkwebview在加载网页之后,会先自动适应缩放一次,
// 如果在这个代理方法中直接return nil,会导致无法按系统建议的缩放比例正确的显示,
// 所以需要利用我们申明的那个bool值来控制
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
    //让它只有在我们允许的时候,才能缩放
    if(self.allowZoom){
        return nil;
    }else{
        return self.wk_WebView.scrollView.subviews.firstObject;
    }
}
  • 在网页加载完之后,(此时系统已经为我们缩放了网页),关闭缩放
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    self.allowZoom = NO;
}
  • 需要时,手动控制:
self.allowZoom = YES;
[self.wk_WebView.scrollView setZoomScale:1.2 animated:NO];


手机基本信息


iOS 将时间NSDate转化为毫秒时间戳

对于将NSDate类型转换为时间戳,有直接转10位数值的时间戳方法,但是没有精确到毫秒,这种数值在转化为 NSDate类型的时候,就会出点儿错,每一个时间的毫秒都是为000的;
因为 [[NSDate date] timeIntervalSince1970] 虽然可以获取到后面的毫秒、微秒 ,但是在保存的时候省略掉了。如一个时间戳不省略的情况下为 1395399556.862046 ,省略掉后为一般所见 1395399556 。所以想取得毫秒时用获取到的时间戳 *1000 ,想取得微秒时 用取到的时间戳 * 1000 * 1000 。这样就解释了上面的10位数值的问题,当你取毫秒的时候,就会变成13位数值了。我想这样大家应该明白了吧!

2个小函数,这2个函数呢,是互逆的:

  • 将时间戳转换为NSDate类型
-(NSDate *)getDateTimeFromMilliSeconds:(long long) miliSeconds
{
    NSTimeInterval tempMilli = miliSeconds;
    NSTimeInterval seconds = tempMilli/1000.0;//这里的.0一定要加上,不然除下来的数据会被截断导致时间不一致
    NSLog(@"传入的时间戳=%f",seconds);
    return [NSDate dateWithTimeIntervalSince1970:seconds];
}
  • 将NSDate类型的时间转换为时间戳,从1970/1/1开始
-(long long)getDateTimeTOMilliSeconds:(NSDate *)datetime
{
    NSTimeInterval interval = [datetime timeIntervalSince1970];
    NSLog(@"转换的时间戳=%f",interval);
    long long totalMilliseconds = interval*1000 ;
    NSLog(@"totalMilliseconds=%llu",totalMilliseconds);
    return totalMilliseconds;
}

解决ios11上 从状态栏下拉或底部栏上滑,跟系统的下拉通知中心手势和上滑控制中心手势冲突

https://blog.csdn.net/auccy/article/details/78978036

你可能感兴趣的:(iOS零碎知识点<初级版>)