iOS开发经验记录

699 阅读5分钟

获取Cell在tableView和当前屏幕中的位置

//获取某个cell在当前tableView上的坐标位置
CGRect rectInTableView = [tableView rectForRowAtIndexPath:indexPath];
//获取cell在当前屏幕的位置
CGRect rectInSuperview = [tableView convertRect:rectInTableView toView:[tableView superview]];

字符串UTF-8 编码与解码

// iOS9之前

// 编码
NSString *encodeStr = [@"你好世界" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

// 解码
NSString *decodeStr = [encodeStr stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

// iOS9之后

// 编码
NSString *encodeStr = [@"你好世界" stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];

// 解码
NSString *decodeStr = [encodeStr stringByRemovingPercentEncoding];

渐变色

extension UIColor {
    class func gradientLeftToRightColor(_ leftColor:UIColor,_ rightColor : UIColor,_ viewW:CGFloat) -> UIColor {
        let size = CGSize.init(width: viewW, height: 1)
        UIGraphicsBeginImageContextWithOptions(size, true, 0)
        let context = UIGraphicsGetCurrentContext()
        let colorspace = CGColorSpaceCreateDeviceRGB()
        let colors = [leftColor.cgColor,rightColor.cgColor]
        let gradient = CGGradient(colorsSpace: colorspace, colors: colors as CFArray, locations: nil)
        context!.drawLinearGradient(gradient!, start: CGPoint(x: 0, y: 0), end: CGPoint(x: size.width, y: 0), options: CGGradientDrawingOptions(rawValue: 0))
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return UIColor.init(patternImage:img!)
    }
}

计算CollectionView高度

 let s = Int(SCREEN_WIDTH/collectionItemWidth)
 var n = dataSource.count/s
 var h: CGFloat
 if n != 0 {
    if n == 1 {
        if dataSource.count > s {
            n += 1
        }
    } else {
        n = dataSource.count % s == 0 ? n : n+1
    }
 
    h = CGFloat(n) * collectionItemHeight + CGFloat(n-1)*collectionItemSpace + topInset + bottomInset
 }
 else {
    h = collectionItemHeight + topInset + bottomInset
 }
 collectionView.snp.remakeConstraints { (make) in
    make.top.equalTo(bottomView).offset(WH(44+10))
    make.left.right.equalTo(bottomView)
    make.height.equalTo(WH(h))
 }
 collectionView.reloadData()

拨打电话的三种方式

1、这种方法,拨打完电话回不到原来的应用,会停留在通讯录里,而且是直接拨打,不弹出提示

NSMutableString * str=[[NSMutableString alloc] initWithFormat:@"tel:%@",@"186xxxx6979"];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];

2、这种方法,打完电话后还会回到原来的程序,也会弹出提示,推荐这种

NSMutableString * str=[[NSMutableString alloc] initWithFormat:@"tel:%@",@"186xxxx6979"];
UIWebView * callWebview = [[UIWebView alloc] init];
[callWebview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:str]]];
[self.view addSubview:callWebview];

3、这种方法也会回去到原来的程序里(注意这里的telprompt),也会弹出提示

NSMutableString * str=[[NSMutableString alloc] initWithFormat:@"telprompt://%@",@"186xxxx6979"];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];

导入openssl库后,file not found

设置Search Path为: $(SRCROOT)/库文件地址 (这个地址是绝对地址)

如果绝对地址为:/Users/zhengtouwang/Downloads/HBRSAHandler-master/HBRSAHandler/HBRSAHandler/HBRSAHandler/openssl/include/openssl

那么Search Path为: $(SRCROOT)/HBRSAHandler/HBRSAHandler/openssl/include

TableView动态改变Cell个数

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    NSIndexPath *path = nil;
    if ([[self.dataArray[indexPath.row] objectForKey:@"Cell"] isEqualToString:@"MainCell"]) {
        path = [NSIndexPath indexPathForItem:(indexPath.row+1) inSection:indexPath.section];
    }else{
        path = indexPath;
    }
    if ([[self.dataArray[indexPath.row] objectForKey:@"isAttached"] boolValue]) {
        // 关闭附加cell
        NSDictionary * dic = @{@"Cell": @"MainCell",@"isAttached":@(NO)};
        self.dataArray[(path.row-1)] = dic;
        [self.dataArray removeObjectAtIndex:path.row];
       
        [self.tableView beginUpdates];
        [self.tableView deleteRowsAtIndexPaths:@[path]  withRowAnimation:UITableViewRowAnimationMiddle];
        [self.tableView endUpdates];
    }else{
        // 打开附加cell
        NSDictionary * dic = @{@"Cell": @"MainCell",@"isAttached":@(YES)};
        self.dataArray[(path.row-1)] = dic;
        NSDictionary * addDic = @{@"Cell": @"AttachedCell",@"isAttached":@(YES)};
        [self.dataArray insertObject:addDic atIndex:path.row];
        
        [self.tableView beginUpdates];
        [self.tableView insertRowsAtIndexPaths:@[path] withRowAnimation:UITableViewRowAnimationMiddle];
        [self.tableView endUpdates];
    } 
}

ScrollView缩放

_scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
// 遵循代理
_scrollView.delegate = self;
// 设置最小缩放倍数
_scrollView.minimumZoomScale = 1;
// 设置最大缩放倍数
_scrollView.maximumZoomScale = 3;
// 代理方法,返回被缩放的view
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{
    return self.PDFView;
}
// 使缩放的View居中
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    CGFloat offsetX = (scrollView.bounds.size.width > scrollView.contentSize.width)?(scrollView.bounds.size.width - scrollView.contentSize.width)/2 : 0.0;
    CGFloat offsetY = (scrollView.bounds.size.height > scrollView.contentSize.height)?(scrollView.bounds.size.height - scrollView.contentSize.height)/2 : 0.0;
    self.PDFView.center = CGPointMake(scrollView.contentSize.width/2 + offsetX,scrollView.contentSize.height/2 + offsetY);
}

判断苹果设备机型

iPhoneWiki

+ (NSString *)getDeviceName
{
    struct utsname systemInfo;
    uname(&systemInfo);
    NSString *deviceString = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
    //iPhone
    if ([deviceString isEqualToString:@"iPhone1,1"])    return @"iPhone 1G";
    if ([deviceString isEqualToString:@"iPhone1,2"])    return @"iPhone 3G";
    if ([deviceString isEqualToString:@"iPhone2,1"])    return @"iPhone 3GS";
    
    ···省略
    
    return deviceString
}

绝对值

  1. abs(a) 处理int类型a的取绝对值

  2. fabsf(a) 处理float类型a的取绝对值

  3. fabs(a) 处理double类型a的取绝对值

金额格式化

NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
NSString *string = [formatter stringFromNumber:[NSNumber numberWithInt:123456789]];
NSLog(@"Formatted number string:%@",string);

输出结果为:~ Formatted number string:123,456,789

其中NSNumberFormatter类有个属性numberStyle,它是一个枚举型,设置不同的值可以输出不同的数字格式。该枚举包括:

enum { 
    NSNumberFormatterNoStyle = kCFNumberFormatterNoStyle, 
    NSNumberFormatterDecimalStyle = kCFNumberFormatterDecimalStyle, 
    NSNumberFormatterCurrencyStyle = kCFNumberFormatterCurrencyStyle, 
    NSNumberFormatterPercentStyle = kCFNumberFormatterPercentStyle, 
    NSNumberFormatterScientificStyle = kCFNumberFormatterScientificStyle, 
    NSNumberFormatterSpellOutStyle = kCFNumberFormatterSpellOutStyle 
}; 

各个枚举对应输出数字格式的效果如下: 
~ Formatted number string:123456789 
~ Formatted number string:123,456,789 
~ Formatted number string:¥123,456,789.00 
~ Formatted number string:-539,222,988% 
~ Formatted number string:1.23456789E8 
~ Formatted number string:一亿二千三百四十五万六千七百八十九 

其中第三项和最后一项的输出会根据系统设置的语言区域的不同而不同。

判断App是否首次打开

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    NSString *key = @"CFBundleShortVersionString";
    // 1.从Info.plist中取出版本号
    NSString *version = [NSBundle mainBundle].infoDictionary[key];
	// 2.从沙盒中取出上次存储的版本号
	NSString *saveVersion = [[NSUserDefaults standardUserDefaults] objectForKey:key];

    if ([version isEqualToString:saveVersion]) { 
    	// 不是第一次使用这个版本
    } else { 
    	// 版本号不一样:第一次使用新版本
    	// 将新版本号写入沙盒
    	[[NSUserDefaults standardUserDefaults] setObject:version forKey:key];
    	[[NSUserDefaults standardUserDefaults] synchronize];
    }
    [self.window makeKeyAndVisible];
    return YES;
}

多线程递归

var arr = [1,2,3,4]
let queue = DispatchQueue(label: "queue")

func removeArr() {
   if arr.count > 0 {
       queue.async {
           sleep(2)
           arr.remove(at: 0)
           print(arr.count)
           removeArr()
       }
   } else {
       queue.async {
           print("remove all")
       }
   }
}
print("remove start")
print(arr.count)
removeArr()

let queue = DispatchQueue(label: "queue", attributes: .concurrent)

func requestData(dict: Dictionary<String,Any>, block: @escaping (()->())) {
    let arr = dict["list"] as! Array<String>
    let key = dict["key"] as! String
    var num = arr.count
    let lock = NSLock()
    for str in arr {
        queue.async {
            sleep(3)
            lock.lock()
            num -= 1
            print("\(str) finished")
            if num == 0 {
                print("\(key) all finished")
                block()
            }
            lock.unlock()
        }
    }
}

var arr = [["key":"task1","list":["task 1.1","task 1.2","task 1.3"]],["key":"task2","list":["task 2.1","task 2.2"]],["key":"task3","list":["task 3.1","task 3.2","task 3.3"]]]
var num = arr.count
let lock = NSLock()
print("all task start")
for sub in arr {
    queue.async {
        requestData(dict: sub, block: {
            lock.lock()
            num -= 1
            if num == 0 {
                print("all task finished")
            }
            lock.unlock()
        })
    }
}

正则查找工程中的所有中文内容

([\u4E00-\u9FA5]+)|([\u4E00-\u9FA5])

po命令打印视图层级

po self.view.recursiveDescription

打印一个类的所有成员变量

unsigned int count;
Ivar *ivars = class_copyIvarList([MJPerson class], &count);
for (int i = 0; i < count; i++) {
		// 取出i位置的成员变量
		Ivar ivar = ivars[i];
		NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
}
free(ivars);

CollectionView拖动排序

@objc private func longPressGesture(_ tap: UILongPressGestureRecognizer) {
    if !isEditing {
        isEditing = !isEditing
        if let cells = collectionView?.visibleCells {
            for cell in cells {
                if let c = cell as? QYChannelViewCell {
                    c.isEditing = isEditing
                }
            }
        }
        if let header = collectionView?.dataSource?.collectionView?(collectionView!, viewForSupplementaryElementOfKind: UICollectionView.elementKindSectionHeader, at: IndexPath(item: 0, section: 0)) as? QYChannelHeaderView {
            header.button.isSelected = isEditing
        }
    }

    let point = tap.location(in: collectionView!)
    switch tap.state {
        case UIGestureRecognizerState.began:
            dragBegan(point: point)
        case UIGestureRecognizerState.changed:
            drageChanged(point: point)
        case UIGestureRecognizerState.ended:
            drageEnded(point: point)
        case UIGestureRecognizerState.cancelled:
            drageEnded(point: point)
        default: break
    }
}

private func getDragingIndexPath(_ point: CGPoint) -> IndexPath? {
    var dragIndexPath: IndexPath? = nil
    if let count = collectionView?.numberOfItems(inSection: 0), count < config.fixedNum {
        return dragIndexPath
    }
    if let indexPathList = collectionView?.indexPathsForVisibleItems {
        for indexP in indexPathList {
            if indexP.section > 0 {
                continue
            }
            if let frame = collectionView?.cellForItem(at: indexP)?.frame, frame.contains(point) {
                if indexP.row > config.fixedNum - 1 {
                    dragIndexPath = indexP
                }
                break
            }
        }
    }
    return dragIndexPath
}

private func getTargetIndexPath(_ point: CGPoint) -> IndexPath? {
    var targetIndexPath: IndexPath? = nil
    if let indexPathList = collectionView?.indexPathsForVisibleItems {
        for indexP in indexPathList {
            // 如果目标IndexPath是自己,或者在第二组,则不需要排序
            if indexP == indexPath || indexP.section > 0 {
                continue
            }
            // 在第一组中找出将被替换位置的Item
            if let frame = collectionView?.cellForItem(at: indexP)?.frame, frame.contains(point) {
                if indexP.row > config.fixedNum - 1 {
                    targetIndexPath = indexP
                }
                break
            }
        }
    }
    return targetIndexPath
}

//MARK: - 长按开始
private func dragBegan(point: CGPoint) {
    indexPath = getDragingIndexPath(point)
    guard let dragingIndexPath = indexPath else { return }
    collectionView?.bringSubviewToFront(dragingItem!)
    if let item = collectionView?.cellForItem(at: dragingIndexPath) as? QYChannelViewCell {
        item.isEditing = true
        dragingItem?.isHidden = true
        dragingItem?.frame = item.frame
        dragingItem?.title = item.title
        dragingItem?.transform = CGAffineTransform(translationX: 1.1, y: 1.1)
    }
}

//MARK: - 长按过程
private func drageChanged(point: CGPoint) {
    guard let dragingIndexPath = indexPath else { return }
    dragingItem?.center = point
    targetIndexPath = getDragingIndexPath(point)
    if let target = targetIndexPath {
        // 更新数据源
        let obj = selectedArr[dragingIndexPath.item]
        selectedArr.remove(at: dragingIndexPath.row)
        selectedArr.insert(obj, at: target.item)
        // 交换位置
        collectionView?.moveItem(at: dragingIndexPath, to: target)
        indexPath = target
    }
}

//MARK: - 长按结束
private func drageEnded(point: CGPoint) {
    guard let dragingIndexPath = indexPath else { return }
    if let endFrame = collectionView?.cellForItem(at: dragingIndexPath)?.frame {
        dragingItem?.transform = CGAffineTransform(translationX: 1.0, y: 1.0)
        UIView.animate(withDuration: 0.25, animations: {
            self.dragingItem?.frame = endFrame
        }) { (finished) in
            self.dragingItem?.isHidden = true
            if let item = self.collectionView?.cellForItem(at: dragingIndexPath) as? QYChannelViewCell {
                item.isEditing = false
            }
            self.indexPath = nil
            self.targetIndexPath = nil
        }
    }
}

获取相机拍摄图片转为Base64传给JS

- (void)catchImageFormCamera:(PGMethod*)commands
{
    NSString* cbId = [commands.arguments objectAtIndex:0];
    NSString * num = [commands.arguments objectAtIndex:1];

    __weak typeof(self) weakSelf = self;
    
    [[[MTMethodListManager alloc] init] catchImagesNum:num images:^(NSArray * _Nonnull arr) {
        NSMutableArray * imageArr = [NSMutableArray array];
        for (int i = 0; i < arr.count; i++) {
            UIImage * img = arr[i];
            NSData *data = UIImageJPEGRepresentation(img, 0.5f);
            NSString * type = [weakSelf imageFormatFromImageData:data];
            // 核心代码,必须要这种格式,不然js不能识别
            NSString *encodedImageStr = [NSString stringWithFormat:@"data:%@;base64,%@",type, [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn]];
            [imageArr addObject:encodedImageStr];
        }
        NSDictionary * dic = @{
                               @"success":@(1),
                               @"imgs":imageArr
                               };
        PDRPluginResult *result = [PDRPluginResult resultWithStatus:PDRCommandStatusOK messageAsDictionary:dic];
        [weakSelf toCallback:cbId withReslut:[result toJSONString]];
    }];
}

//图片格式检查
- (NSString *)imageFormatFromImageData:(NSData *)imageData{
    
    uint8_t first_byte;
    [imageData getBytes:&first_byte length:1];
    switch (first_byte) {
        case 0xFF:
            return @"image/jpeg";
        case 0x89:
            return @"image/png"; // https://www.w3.org/TR/PNG-Structure.html
        case 0x47:
            return @"image/gif";
        case 0x49:
        case 0x4D:
            return @"image/tiff";
        case 0x52:
            if ([imageData length] < 12) {
                return nil;
            }
            
            NSString *dataString = [[NSString alloc] initWithData:[imageData subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
            if ([dataString hasPrefix:@"RIFF"] && [dataString hasSuffix:@"WEBP"]) {
                return @"image/webp";
            }
            
            return nil;
    }
    return nil;
}