【iOS】常用技术点小记1

layer.shouldRasterize

shouldRasterize instructs Core Animation to cache the layer contents as an image.
栅格化,设置为true则Core Animation将layer.contents渲染成一张图片缓存下来备用,不再刷新重绘视图,从而提高效率

layer.shouldRasterize = true 
layer.rasterizationScale = UIScreen.main.scale

layer.anchorPoint

设置layer.anchorPoint.x 要在设置layer.frame之前

UIViewController-自定义初始化方法

init(sideMenu: UIViewController, center: UIViewController) {
    menuViewController = sideMenu
    centerViewController = center
    super.init(nibName: nil, bundle: nil)
  }

CAGradientLayer

【iOS】常用技术点小记1_第1张图片
CAGradientLayer

debug UIView Hierarchy

【iOS】常用技术点小记1_第2张图片
这将会显示所有view的层级结构,包括超出屏幕的、透明的、隐藏的

NSLayoutConstraint

    // fixed width constraint:
    let _ = NSLayoutConstraint(item: iv, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 0, constant: 50)
    
    // relative width constraint
    let widthCons = NSLayoutConstraint(item: iv, attribute: .width, relatedBy: .equal, toItem: view, attribute: .width, multiplier: 1/3, constant: -50)

代码查找约束

【iOS】常用技术点小记1_第3张图片
NSLayoutConstraint
for cons in titleLabel.superview!.constraints {
        if cons.firstItem as? UIView == titleLabel && cons.firstAttribute == .centerX{
            cons.constant = isMenuOpen ? 100 : 0
            break
        }
}

swift3 CGAffineTransform

view.transform = CGAffineTransform(scaleX: 1, y: 0.1)
.concatenating(CGAffineTransform(translationX: 0, y: off))

Array扩展

extension Array where Element: Comparable{

}

guard & where 配合使用

guard let dividend = dividend where dividend != 1, let divisor = divisor where divisor != 0 else {
     return .None
}

播放声音

AudioServicesPlayAlertSound(kSystemSoundID_Vibrate) //震动

获取String高度/宽度

let rect = (textView.text as NSString).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: textView.font!], context: nil)

UIColor-16进制

public convenience init(hex: Int){
    self.init(red: CGFloat((hex >> 16) & 0xff), green: CGFloat((hex >> 8) & 0xff), blue: CGFloat(hex & 0xff), alpha: 1)
}

UITableView分割线设置问题

// 注意在initWithFrame/initWvithCoder里设置才起作用
v.separatorStyle = .none

UITableView/UICollectionView阴影设置问题

v.clipsToBounds = false //必须为false才行,默认是true
// cell也要与view设置同样大的圆角,同时bgcolor设置为nil

UICollectionViewCell/UITableViewCell布局layoutMargins设置注意

// 必须设置了contentView.layoutMargins才起作用
contentView.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

Xcode8一直打印AQDefaultDevice (173): skipping input stream

【iOS】常用技术点小记1_第4张图片
Product -> Scheme -> Edit Scheme

libc++abi.dylib`__cxa_throw: 使用[AVAudioPlayer play]会产生__cxa_throw异常

【iOS】常用技术点小记1_第5张图片
All包含__cxa_throw异常捕获
【iOS】常用技术点小记1_第6张图片
只捕获objc异常

Swfit.AnyClass获取

public var appName: String{
        guard let dict = Bundle.main.infoDictionary,
            let name = dict["CFBundleName"] as? String else{
                return ""
        }
        return name
}
func SwiftClassFromNameString(_ className: String) -> AnyClass?{
    let name = "\(appName).\(className)"//这里要加上AppName.
    return NSClassFromString(name)
}

WKWebView与JS交互


import UIKit
import WebKit

class ViewController: UIViewController, WKScriptMessageHandler {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let config = WKWebViewConfiguration()
        let user = WKUserContentController()
        user.add(self, name: "Test")
        config.userContentController = user
        let wk = WKWebView(frame: view.bounds, configuration: config)
        
        wk.load(URLRequest(url: Bundle.main.url(forResource: "test.html", withExtension: nil)!))
        
        view.addSubview(wk)
    }

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        print(message.body)
    }

}

JS判断客户端类型

// 01

// 02

UIWebView/WKWebView视频播放问题

allowsInlineMediaPlayback = true
mediaPlaybackRequiresUserAction = !UIDevice.current.isIpad //iPad得设置为false

状态栏隐藏

app.setStatusBarHidden(true, with: .fade) //9.0以前
// 或
override var prefersStatusBarHidden: Bool{
     return true
}
全局配置

View指定圆角

    @IBInspectable
    public var topLeft: Bool = false{
        didSet{
            setNeedsDisplay()
        }
    }
    
    @IBInspectable
    public var bottomLeft: Bool = false{
        didSet{
            setNeedsDisplay()
        }
    }
    
    @IBInspectable
    public var topRight: Bool = false{
        didSet{
            setNeedsDisplay()
        }
    }
    
    @IBInspectable
    public var bottomRight: Bool = false{
        didSet{
            setNeedsDisplay()
        }
    }
    
    @IBInspectable
    public var radius: CGFloat = 0{
        didSet{
            setNeedsDisplay()
        }
    }
override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        var corners: UIRectCorner = []
        if topLeft {
            corners.insert(.topLeft)
        }
        if bottomLeft {
            corners.insert(.bottomLeft)
        }
        if topRight {
            corners.insert(.topRight)
        }
        if bottomRight {
            corners.insert(.bottomRight)
        }
        let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        let maskLayer = CAShapeLayer()
        maskLayer.path = path.cgPath
        maskLayer.frame = rect
        layer.mask = maskLayer
    }

clipsToBounds和layer.masksToBounds对shadow效果的影响

  • 注意true、false值对shadow效果的影响
  • 注意layer.cornerRadius和其他border属性对shadow的影响

iOS10.3应用内动态替换AppIcon

【iOS】常用技术点小记1_第7张图片
Info.plist配置
    @IBOutlet weak var segment: UISegmentedControl!
    
    fileprivate let icons = [
        "AppIcon1",
        "AppIcon2"
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()

       // 上次设置的图标名,默认为nil
        if let name = UIApplication.shared.alternateIconName,
            let i = icons.index(of: name){
            print(name)
            segment.selectedSegmentIndex = i+1
        }
    }

    @IBAction func changeIcon(_ sender: UISegmentedControl){
        let app = UIApplication.shared
        let can = app.supportsAlternateIcons
        if can {
            let index = sender.selectedSegmentIndex
            // nil时为默认图标Primary Icon
            let name =  index == 0 ? nil : icons[index-1] 
            
            app.setAlternateIconName(name, completionHandler: { (error) in
                let info = error != nil ? "失败" : "成功"
                print("更好图标: \(info)")
            })
        }
        else{
            print("不支持 AppIcon 动态替换")
        }
    }

UIView按下样式

    override func touchesBegan(_ touches: Set, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        // 背景色
        backgroundColor = mcolors.gray.withAlphaComponent(0.2)
    }
    
    override func touchesEnded(_ touches: Set, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        // 背景色
        backgroundColor = mcolors.white
    }

UICollectionViewCell的shadow问题


    cell.layer.masksToBounds = false; //必须为false否者阴影没有效果

    cell.layer.contentsScale = [UIScreen mainScreen].scale;

    cell.layer.shadowOpacity = 0.75f;

    cell.layer.shadowRadius = 4.0f;

    cell.layer.shadowOffset = CGSizeMake(0,0);

    cell.layer.shadowPath = [UIBezierPath bezierPathWithRect:cell.bounds].CGPath;

    //设置缓存
    cell.layer.shouldRasterize = YES;

    //设置抗锯齿边缘
    cell.layer.rasterizationScale = [UIScreen mainScreen].scale;

点击status bar上的“返回 xxx”应用的处理,刷新controller数据

  • 注意,之前的vc将不再调用viewWillAppear方法,需要手动利用通知中心处理事件
func applicationWillEnterForeground(_ application: UIApplication) {
        // 通知
        NotificationCenter.default.post(name: .xxx, object: nil)
}

The Copy Bundle Resources build phase contains this target's Info.plist问题

【iOS】常用技术点小记1_第8张图片
删除plist

关于输入框键盘遮挡动画的一种不错方法

  • 封装包含或基于UITextField的控件
// 加入键盘监听
[[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWillShow:)
                                                     name:UIKeyboardWillShowNotification
                                                   object:nil];
        
[[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWillHide:)
                                                     name:UIKeyboardWillHideNotification
                                                   object:nil];
// 处理键盘事件
-(void)keyboardWillShow: (NSNotification *)noti{
    CGRect frame = [noti.userInfo[@"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
    CGFloat y = UIScreen.mainScreen.bounds.size.height - frame.size.height;
    
    CGFloat bottom = CGRectGetMaxY(self.frame);
    
// 只有输入框被键盘遮挡时才调用
    if (bottom > y) {
        
        CGFloat off = bottom - y;
        CGFloat duration = [noti.userInfo[@"UIKeyboardAnimationDurationUserInfoKey"] floatValue];
        [_delegate inputView:self shouldUpdatePositionY:off duration:duration];
    }
}

-(void)keyboardWillHide: (NSNotification *)noti{
    CGFloat duration = [noti.userInfo[@"UIKeyboardAnimationDurationUserInfoKey"] floatValue];
    
    [_delegate inputView:self shouldUpdatePositionY:0 duration:duration];
}

// 代理方法,改变约束值,进行动画
-(void)inputView:(HYLoginInputView *)inputView shouldUpdatePositionY:(CGFloat)offsetY duration:(NSTimeInterval)duration{
    
// 存储变量,变化值
    _offYKeyboard = offsetY;
    
    [UIView animateWithDuration:duration animations:^{
        [self layoutSubviews]; //注意在UIViewController中调用[self layoutIfNeeded];进行重新布局
    }];
}

// 更新布局
-(void)layoutSubviews{
    [super layoutSubviews];

    [_userNameInput mas_updateConstraints:^(MASConstraintMaker *make) {
        make.leading.equalTo(@(offX));
        make.trailing.equalTo(@(-offX));
        make.centerY.mas_equalTo(self).offset(-_offYKeyboard); //键盘变化数值
        make.height.equalTo(@80);
    }];

}

关于Assets中image的Slicing

【iOS】常用技术点小记1_第9张图片
示例

[The app delegate must implement the window property if it wants to use a main interface]解决

  • ✅ AppDelegate中设置
   var window: UIWindow!
   func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        window.rootViewController = xxx
        return true
    }
  • 不要试图手动维护某个window,并设置为keyWindow,否则导致navigationBar的高度为44或横竖屏显示错误的问题,如:
private let window = UIWindow(frame: UIScreen.main.bounds)
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        window.rootViewController = xxx
        return true
    }
【iOS】常用技术点小记1_第10张图片
然后设置`Main Interface`为某个storyboard

[POST git-receive-pack (chunked)]解决

【iOS】常用技术点小记1_第11张图片
SourceTree 操作1
【iOS】常用技术点小记1_第12张图片
SourceTree 操作2
[http] 
    postBuffer = 524288000

默认Xcode设置

xcode-select --print-path //查看原先路径

sudo xcode-select --switch
/Applications/Xcode8.3.2/Xcode8.3.2.app/Contents/Developer //新路径

横竖屏控制UICollectionView的不同尺寸展示

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

//改变布局 size,走UICollectionViewDelegateFlowLayout方法
        collectionView?.collectionViewLayout.invalidateLayout() 
//刷新数据 cell,走UICollectionViewDataSource方法
        collectionView?.reloadData() 
    }

// 尺寸控制
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
        let num: CGFloat = app.statusBarOrientation.isLandscape ? 5 : 4
        let w = (view.bounds.width - (num+1)*itemSpace) / num

        return CGSize(width: w, height: w)
    }

// 显示个数控制
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        let num = app.statusBarOrientation.isLandscape ? 6 : 5
        let count = data[section].items.count
        if count > num {
            return num
        }
        return count
    }

It looks like git-am is in progress. Cannot rebase

cd 到工程根目录
执行rm -rf .git/rebase-apply

利用CIDetector识别图中的二维码

  • 只适用于真机
  • iOS5.0及以上
// 截屏图片
let screen = UIScreen.mainScreen()
        UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, false, screen.scale);
        self.view.layer.renderInContext(UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext();
// 识别
        let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])
        let ciimage = CIImage(CGImage: image.CGImage!)

        let features = detector.featuresInImage(ciimage)
        if let feature = features.first as? CIQRCodeFeature{
            let str = feature.messageString
            print("result: \(str)")
        }

HUD提示view被键盘遮挡的解决方法

  • 原因:将view加在了UIApplication.shared.keyWindow上,而键盘弹出时又是一个新的类型为UIRemoteKeyboardWindow的窗口
  • 解决:每次去动态计算获取相应的keyWindow()
func keyWindow() -> UIWindow{
    var window = app.keyWindow!
    for w in app.windows{
        let name = NSStringFromClass(w.classForCoder)
        if name == "UIRemoteKeyboardWindow"{
            window = w
            break
        }
    }
    return window
}

TableView删除cell问题小记

cell.configureCell(data: classes[indexPath.row]) {[unowned self] (data) in
            var index: IndexPath!
// 注意这里存储的indexPath将不再有用,因为随着cell的删除,indexPath值是变化的,
// 除非你自己手动在删除cell后reloadData(),但那样会失去动画效果
            for (i,e) in self.classes.enumerated(){
                if e.id == data.id{
                    index = IndexPath(row: i, section: 0)
                    break
                }
            }
            self.classes = self.classes.filter{
                $0.id != data.id
            }
            self.tableView.deleteRows(at: [index], with: .automatic)
        }

TabBar 相关尺寸

icon: <= 30x30px 一般: 26x26px
height: 49px = 48px + 1px分割线
fontSize: 12px

UITextField限定输入长度

extension RegisterVC: UITextFieldDelegate{
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        
        if string.length == 0 { //删除键
            return true
        }
        
        if textField == xxxTF && text.length > 10 {
            return false
        }
        return true
    }
}

Swift3.0 private、fileprivate、internal、public、open修饰符

  • private 定义的方法和属性,只能是该类的对象可以访问,类的extension子类对象中均无法访问。

  • fileprivate 顾名思义,只在当前的.swift文件中是私有的,可以在多个文件中定义同名的变量值。

  • internal 是默认的修饰符,可在当前整个工程源码所在的所有文件中均可以访问,常用来定义一些全局的宏变量或非法,通常省略不写。

  • public 只能在module内部被inherit和override

  • open 任何地方均可访问和override

Swift 参数是Float或Int类型的函数定义

func fo(_ value: T) where T: ExpressibleByIntegerLiteral, T: ExpressibleByFloatLiteral {
        print(value)
}

Swift 输出函数名、类名、方法所在行号

func fo(name: String = #function, line: Int = #line, file: String = #file) {
        print("\(file.lastPathComponent.deletingPathExtension) - \(name) - \(line)")
}

Swift3.0生成二维码图片


public extension UIImage {  
      
    public class func createQRCode(code: String, width: CGFloat, height: CGFloat) -> UIImage? {  
        let data = code.data(using: String.Encoding.isoLatin1, allowLossyConversion: false)  
        if let filter = CIFilter(name: "CIQRCodeGenerator") {  
            filter.setValue(data, forKey: "inputMessage")  
            filter.setValue("H", forKey: "inputCorrectionLevel")  
//            inputCorrectionLevel 是一个单字母(@"L", @"M", @"Q", @"H" 中的一个),表示不同级别的容错率,默认为 @"M"  
//            错误修正容量 L水平 7%的字码可被修正  
//            M水平 15%的字码可被修正  
//            Q水平 25%的字码可被修正  
//            H水平 30%的字码可被修正  
//            所以很多二维码的中间都有头像之类的图片但仍然可以识别出来就是这个原因  
            if let QRCodeImage = filter.outputImage {  
                //消除模糊  
                let scaleX = width/QRCodeImage.extent.size.width  
                let scaleY = height/QRCodeImage.extent.size.height  
                let transformedImage = QRCodeImage.applying(CGAffineTransform.init(scaleX: scaleX, y: scaleY))  
                  
                return UIImage(ciImage: transformedImage)  
            } else {  
                return nil  
            }  
        }else {  
            return nil  
        }  
    }  
}  

StoryBoard上的UITableViewController中的static cell无法设置layoutMargins

如图设置cell元素约束的时候,必须考虑默认的值为8的margin

改变无效
【iOS】常用技术点小记1_第13张图片
.leading = 12+8

关于storyboard的unwind

//unwind一定是定义在当前页面vc的来源vc中,
//如vc2是由vc1导航push进来的,那么该unwind方法就应该在vc1中,而不是v2中

@IBAction func unwindForVC1(_ sender: UIStoryboardSegue){
        
}

MathJax数学表达式解析

hidesBottomBarWhenPushed=true使得view在布局变化时往下掉的问题

【iOS】常用技术点小记1_第14张图片
问题

解决方法:

tabBar.isTranslucent = false //设置为不透明
同样适合解决:navigationController.toolBar.hidden = false出现的同样问题

xib或storyboard自动布局

当需要根据子view的高度压缩或拉伸的时候,就得设置各个view的Hugging PriorityCompression Resistance Priority
值越大越难被拉伸Hugging或压缩Compression

要实现如图的效果则需要如下设置:


【iOS】常用技术点小记1_第15张图片
拉伸-压缩布局

则布局设置优先级为:

【iOS】常用技术点小记1_第16张图片
green view
【iOS】常用技术点小记1_第17张图片
skin view

点击view隐藏键盘的2种方法

//1. 当前view的touchesBegan方法
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        endEditing(true)
    }

//2. 找不到controller或editing事件不在当前view中
UIApplication.shared.keyWindow?.endEditing(true)

brew update

sudo chmod -R g+w /usr/local
brew update

libxml2.tbd库引入问题解决

报错: 'libxml.h' file not found with include, use "quotes" instead.

解决方法

【iOS】常用技术点小记1_第18张图片
在`Build Settings`里设置

Podfile编写

inhibit_all_warnings!

target 'DemoKissXML' do
    platform :ios, '7.0'
    pod 'KissXML'
end
  • 执行命令:pod setup ,再执行 pod install,每次改变Podfile内容,执行pod update

StoryBoard上自动布局约束设置

【iOS】常用技术点小记1_第19张图片
设置间距space约束
【iOS】常用技术点小记1_第20张图片
space to

指定UIView的LayoutMargins

// 默认为8dp - 定义了view的内边距
view.layoutMargins = UIEdgeInsetsMake(20, 20, 20, 20)
【iOS】常用技术点小记1_第21张图片
storyboard设置

UIWebView和JS交互一种新方案:

// 通过JS向body中新插入一个iframe标签,就会触发OC的webView shouldStartLoadWithRequest方法,
// 从而传递来自html的参数达到和UIWebView交互的目的

//JS方法
      

// OC
- (BOOL)webView:(UIWebView *)webView 
shouldStartLoadWithRequest:(NSURLRequest *)request 
navigationType:(UIWebViewNavigationType)navigationType {
    NSURL * url = [request URL];
    if ([[url scheme] isEqualToString:@"xxx"]) {
        // do something...

        return NO;
    }
    return YES;
}

AppStore构建版本显示【缺少合规证明】

在info.plist中添加下面的键值
ITSAppUsesNonExemptEncryption

Masonry中iOS10宏支持iOS7

// 在MASUtilities.h中加入
#define NS_NOESCAPE __attribute__((noescape))

自定义UIWebView右键菜单

// 自定义MyWebView,重写init方法
-(instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
// 这里自定义menu选项
        UIMenuItem *copy = [[UIMenuItem alloc] initWithTitle:@"拷贝" action:@selector(copy:)];
        UIMenuController *menu = [UIMenuController sharedMenuController];
        [menu setMenuItems:@[copy]];
    }
    return self;
}

// 重写方法
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
// 除了自定义menu的事件方法外,其他都不执行
    if (action == @selector(copy:)) {
        return YES;
    }
    return NO;
}

// copy事件
- (void)copy: (id)sender{
   [self copy: sender]; 
}

UIWebView网页文字下划线、标记颜色JS

function stylizeHighlightedString() {

    var range = window.getSelection().getRangeAt(0);
    var selectionContents = range.extractContents();
    var span = document.createElement("span");

    span.appendChild(selectionContents);

// 点击标记的文字事件
    span.setAttribute("onclick","alert('划过线的文字');");
// 标记颜色
    // span.style.backgroundColor  = "rgba(255,0,0,0.5)";
// 下划线
    span.style.borderBottom  = "2px solid red";

    range.insertNode(span);
}

UIWebView背景透明

// 前提是不能设置webview的分页模式为水平模式【.LeftToRight或.RightToLeft】
webView.backgroundColor = [UIColor clearColor];
webView.opaque = NO;

iOS设备分辨率【全】

【iPhone】
iPhone 1G 320x480
iPhone 3G 320x480
iPhone 3GS 320x480
iPhone 4 640x960
iPhone 4S 640x960
iPhone 5 640x1136
iPhone 5S 640x1136
iPhone 5C 640x1136
iPhone 6 750x1334
iPhone 6 Plus 1080x1920 (开发应按照1242x2208适配)
iPhone 6S 750x1334
iPhone 6S Plus 1080x1920 (开发应按照1242x2208适配)

【iPod Touch】
iPod Touch 1G 320x480
iPod Touch 2G 320x480
iPod Touch 3G 320x480
iPod Touch 4G 640x960
iPod Touch 5G 640x1136

【iPad】
iPad 1 1024x768
iPad 2 1024x768
The New iPad 2048x1536
iPad mini 1024x768
iPad 4 2048x1536
iPad Air 2048x1536
iPad mini 2 2048x1536
iPad Air 2 2048x1536
iPad mini 3 2048x1536
iPad mini 4 2048x1536
iPad Pro 2732x2048

Bugly自动上传符号表脚本

#!/bin/sh
#
# Copyright 2016 Bugly, Tencent. All rights reserved.
#
# V1.4.0
#
# 2016.08.03
#
#
#
######################################################
# 1. 脚本集成到Xcode工程的Target
######################################################
#
# --- Copy the SCRIPT to the Run Script of Build Phases in the Xcode project ---
#
# #
BUGLY_APP_ID="" #必填 appId
# #
BUGLY_APP_KEY="" #必填 appKey
# #
BUNDLE_IDENTIFIER="" #必填 bundleId
# #
UPLOAD_DSYM_ONLY=1
#
# # 脚本默认配置的版本格式为CFBundleShortVersionString(CFBundleVersion),  如果你修改默认的版本格式, 请设置此变量, 如果不想修改, 请忽略此设置
# CUSTOMIZED_APP_VERSION=""
#
# # Debug模式编译是否上传,1=上传 0=不上传,默认不上传
# UPLOAD_DEBUG_SYMBOLS=0
#
# # 模拟器编译是否上传,1=上传 0=不上传,默认不上传
# UPLOAD_SIMULATOR_SYMBOLS=0
#
# #只有Archive操作时上传, 1=支持Archive上传 0=所有Release模式编译都上传
# UPLOAD_ARCHIVE_ONLY=1
#
# #
# source dSYMUpload.sh
#
# --- END OF SCRIPT ---
#
#
#
#
#######################################################
# 2. 脚本根据输入参数处理
#######################################################
#
# #命令行下输入应用基本信息, .dSYM文件的父目录路径, 输出文件目录即可
#
# sh dSYMUpload.sh      
#
# #
#
# #注意:
# # 1. dSYMUpload.sh会调用buglySymboliOS.jar进行.dSYM解析,所以依赖Java运行时环境
# # 2. dSYMUpload.sh和buglySymboliOS.jar的文件路径需一致
#
#

#
# --- CONTENT OF SCRIPT ---
#

# Bugly服务域名
BUGLY_DSYM_UPLOAD_DOMAIN="api.bugly.qq.com"

# 注意jar工具的路径跟dSYMUpload.sh脚本路径一致, 请务必保证jar路径的正确性
BUGLY_SYMBOL_JAR_PATH="dsymtool/buglySymboliOS.jar"
# 查找添加到系统目录的jar工具
if [ ! -f "${BUGLY_SYMBOL_JAR_PATH}" ]; then
BUGLY_SYMBOL_JAR_PATH="$HOME/bin/buglySymboliOS.jar"
fi

# 打印错误信息
function exitWithMessage(){
    echo "--------------------------------"
    echo "${1}"
    echo "--------------------------------"
    exit ${2}
}

# 上传bSYMBOL文件
function dSYMUpload() {
    P_APP_ID="$1"
    P_APP_KEY="$2"
    P_APP_BUNDLE_ID="$3"
    P_APP_VERSION="$4"
    P_BSYMBOL_ZIP_FILE="$5"
    
    #
    P_BSYMBOL_ZIP_FILE_NAME=${P_BSYMBOL_ZIP_FILE##*/}
        P_BSYMBOL_ZIP_FILE_NAME=${P_BSYMBOL_ZIP_FILE_NAME//&/_}
            P_BSYMBOL_ZIP_FILE_NAME="${P_BSYMBOL_ZIP_FILE_NAME// /_}"
            
            DSYM_UPLOAD_URL="https://${BUGLY_DSYM_UPLOAD_DOMAIN}/openapi/file/upload/symbol?app_id=${P_APP_ID}&app_key=${P_APP_KEY}"
            echo "dSYM upload url: ${DSYM_UPLOAD_URL}"
            
            echo "-----------------------------"
            STATUS=$(/usr/bin/curl -k "${DSYM_UPLOAD_URL}" --form "api_version=1" --form "app_id=${P_APP_ID}" --form "app_key=${P_APP_KEY}" --form "symbolType=2"  --form "bundleId=${BUNDLE_IDENTIFIER}" --form "productVersion=${BUGLY_APP_VERSION}" --form "fileName=${P_BSYMBOL_ZIP_FILE_NAME}" --form "file=@${P_BSYMBOL_ZIP_FILE}" --verbose)
            echo "-----------------------------"
            
            UPLOAD_RESULT="FAILTURE"
            echo "Bugly server response: ${STATUS}"
            if [ ! "${STATUS}" ]; then
            echo "Error: Failed to upload the zip archive file."
            elif [[ "${STATUS}" == *"{\"reponseCode\":\"0\"}"* ]]; then
            echo "Success to upload the dSYM for the app [${BUNDLE_IDENTIFIER} ${BUGLY_APP_VERSION}]"
            UPLOAD_RESULT="SUCCESS"
            else
            echo "Error: Failed to upload the zip archive file to Bugly."
            fi
            
            #Remove temp dSYM archive
            #echo "Remove temporary zip archive: ${DSYM_ZIP_FPATH}"
            #/bin/rm -f "${DSYM_ZIP_FPATH}"
            
            if [ "$?" -ne 0 ]; then
            exitWithMessage "Error: Failed to remove temporary zip archive." 0
            fi
            
            echo "--------------------------------"
            echo "${UPLOAD_RESULT} - dSYM upload complete."
            
            if [[ "${UPLOAD_RESULT}" == "FAILTURE" ]]; then
            echo "--------------------------------"
            echo "Failed to upload the dSYM"
            echo "Please check the script and try it again."
            fi
        }
        
        # .dSYM解析为bSYMBOL文件
        function dSYMParse() {
            DSYM_FILE="$1"
            DSYM_SYMBOL_FILE="$2"
            
            echo "--------------------------------"
            echo "Extract symbol info from .dSYM file. to ${DSYM_SYMBOL_FILE}"
            (/usr/bin/java -Xms512m -Xmx1024m -Dfile.encoding=UTF8 -jar "${BUGLY_SYMBOL_JAR_PATH}" -i "${DSYM_FILE}" -o "${DSYM_SYMBOL_FILE}" ) || exitWithMessage "Error: Failed to extract symbols." 1
            echo "--------------------------------"
            
        }
        
        # 执行
        function run() {
            
            CONFIG_BUGLY_APP_ID="$1"
            CONFIG_BUGLY_APP_KEY="$2"
            
            CONFIG_BUGLY_APP_BUNDLE_IDENTIFIER="$3"
            CONFIG_BUGLY_APP_VERSION="$4"
            CONFIG_DSYM_SOURCE_DIR="$5"
            CONFIG_DSYM_DEST_DIR="$6"
            CONFIG_UPLOAD_DSYM_ONLY="$7"
            
            # 检查必须参数是否设置
            if [ ! "${CONFIG_BUGLY_APP_ID}" ]; then
            exitWithMessage "Error: Bugly App ID not defined. Please set 'BUGLY_APP_ID' " 0
            fi
            
            if [[ "${CONFIG_BUGLY_APP_ID}" == *"App ID"* ]]; then
            exitWithMessage "Error: Bugly App ID not defined." 0
            fi
            
            if [ ! "${CONFIG_BUGLY_APP_KEY}" ]; then
            exitWithMessage "Error: Bugly App Key not defined." 0
            fi
            
            if [ ! "${CONFIG_BUGLY_APP_BUNDLE_IDENTIFIER}" ]; then
            exitWithMessage "Error: Bundle Identifier not defined." 0
            fi
            
            if [ ! "${CONFIG_BUGLY_APP_VERSION}" ]; then
            exitWithMessage "Error: App Version not defined." 0
            fi
            
            if [ ! -e "${CONFIG_DSYM_SOURCE_DIR}" ]; then
            exitWithMessage "Error: Invalid dir ${CONFIG_DSYM_SOURCE_DIR}" 0
            fi
            
            if [ ! "${CONFIG_DSYM_DEST_DIR}" ]; then
            exitWithMessage "Error: Invalid dir ${CONFIG_DSYM_DEST_DIR}" 0
            fi
            
            if [ ! -e "${CONFIG_DSYM_DEST_DIR}" ]; then
            mkdir ${CONFIG_DSYM_DEST_DIR}
            fi
            
            DSYM_FOLDER="${CONFIG_DSYM_SOURCE_DIR}"
            IFS=$'\n'
            
            echo "Scaning dSYM FOLDER: ${DSYM_FOLDER} ..."
            RET="F"
            
            #
            for dsymFile in $(find "$DSYM_FOLDER" -name '*.dSYM'); do
            RET="T"
            echo "Found dSYM file: $dsymFile"
            
            DSYM_FILE_NAME=${dsymFile##*/}
                DSYM_SYMBOL_ZIP_FILE_NAME="${DSYM_FILE_NAME}.zip"
                DSYM_SYMBOL_ZIP_FILE_NAME="${DSYM_SYMBOL_ZIP_FILE_NAME// /_}"
                DSYM_SYMBOL_ZIP_FILE=${CONFIG_DSYM_DEST_DIR}/${DSYM_SYMBOL_ZIP_FILE_NAME}
                
                if [ $CONFIG_UPLOAD_DSYM_ONLY -eq 1 ]; then
                if [ -e $DSYM_SYMBOL_ZIP_FILE ]; then
                rm -f $DSYM_SYMBOL_ZIP_FILE
                fi
                # 如果只上传dSYM,直接压缩dSYM目录
                zip -r -j $DSYM_SYMBOL_ZIP_FILE $dsymFile -x *.plist
                else
                # 使用符号表工具来生成Symbol文件
                dSYMParse $dsymFile $DSYM_SYMBOL_ZIP_FILE
                fi
                
                # 上传
                dSYMUpload $CONFIG_BUGLY_APP_ID $CONFIG_BUGLY_APP_KEY $CONFIG_BUGLY_APP_BUNDLE_IDENTIFIER $CONFIG_BUGLY_APP_VERSION $DSYM_SYMBOL_ZIP_FILE
                done
                
                if [ $RET = "F" ]; then
                exitWithMessage "No .dSYM found in ${DSYM_FOLDER}" 0
                fi
            }
            
            # 在Xcode工程中执行
            function runInXcode(){
                echo "Uploading dSYM to Bugly in Xcode ..."
                
                echo "Info.Plist : ${INFOPLIST_FILE}"
                
                BUNDLE_VERSION=$(/usr/libexec/PlistBuddy -c 'Print CFBundleVersion' "${INFOPLIST_FILE}")
                BUNDLE_SHORT_VERSION=$(/usr/libexec/PlistBuddy -c 'Print CFBundleShortVersionString' "${INFOPLIST_FILE}")
                
                # 组装Bugly默认识别的版本信息(格式为CFBundleShortVersionString(CFBundleVersion), 例如: 1.0(1))
                if [ ! "${CUSTOMIZED_APP_VERSION}" ]; then
                BUGLY_APP_VERSION="${BUNDLE_SHORT_VERSION}(${BUNDLE_VERSION})"
                else
                BUGLY_APP_VERSION="${CUSTOMIZED_APP_VERSION}"
                fi
                
                echo "--------------------------------"
                echo "Prepare application information."
                echo "--------------------------------"
                
                echo "Product Name: ${PRODUCT_NAME}"
                echo "Bundle Identifier: ${BUNDLE_IDENTIFIER}"
                echo "Version: ${BUNDLE_SHORT_VERSION}"
                echo "Build: ${BUNDLE_VERSION}"
                
                echo "Bugly App ID: ${BUGLY_APP_ID}"
                echo "Bugly App key: ${BUGLY_APP_KEY}"
                echo "Bugly App Version: ${BUGLY_APP_VERSION}"
                
                echo "--------------------------------"
                echo "Check the arguments ..."
                
                ##检查模拟器编译是否允许上传符号
                if [ "$EFFECTIVE_PLATFORM_NAME" == "-iphonesimulator" ]; then
                if [ $UPLOAD_SIMULATOR_SYMBOLS -eq 0 ]; then
                exitWithMessage "Warning: Build for simulator and skipping to upload. \nYou can modify 'UPLOAD_SIMULATOR_SYMBOLS' to 1 in the script." 0
                fi
                fi
                
                ##检查是否是Release模式编译
                if [ "${CONFIGURATION=}" == "Debug" ]; then
                if [ $UPLOAD_DEBUG_SYMBOLS -eq 0 ]; then
                exitWithMessage "Warning: Build for debug mode and skipping to upload. \nYou can modify 'UPLOAD_DEBUG_SYMBOLS' to 1 in the script." 0
                fi
                fi
                
                ##检查是否Archive操作
                if [ $UPLOAD_ARCHIVE_ONLY -eq 1 ]; then
                if [[ "$TARGET_BUILD_DIR" == *"/Archive"* ]]; then
                echo "Archive the package"
                else
                exitWithMessage "Warning: Build for NOT Archive mode and skipping to upload. \nYou can modify 'UPLOAD_ARCHIVE_ONLY' to 0 in the script." 0
                fi
                fi
                
                #
                run ${BUGLY_APP_ID} ${BUGLY_APP_KEY} ${BUNDLE_IDENTIFIER} ${BUGLY_APP_VERSION} ${DWARF_DSYM_FOLDER_PATH} ${BUILD_DIR}/BuglySymbolTemp ${UPLOAD_DSYM_ONLY}
            }
            
            # 根据Xcode的环境变量判断是否处于Xcode环境
            INFO_PLIST_FILE="${INFOPLIST_FILE}"
            
            BuildInXcode="F"
            if [ -f "${INFO_PLIST_FILE}" ]; then
            BuildInXcode="T"
            fi
            
            if [ $BuildInXcode = "T" ]; then
            runInXcode
            else
            echo "\nUsage: dSYMUpload.sh       [upload_dsym_only]\n"
            # 你可以在此处直接设置BuglyAppID和BuglyAppKey,排除不常变参数的输入
            BUGLY_APP_ID="$1"
            BUGLY_APP_KEY="$2"
            BUNDLE_IDENTIFIER="$3"
            BUGLY_APP_VERSION="$4"
            DWARF_DSYM_FOLDER_PATH="$5"
            SYMBOL_OUTPUT_PATH="$6"
            UPLOAD_DSYM_ONLY=$7
            run ${BUGLY_APP_ID} ${BUGLY_APP_KEY} ${BUNDLE_IDENTIFIER} ${BUGLY_APP_VERSION} ${DWARF_DSYM_FOLDER_PATH} ${SYMBOL_OUTPUT_PATH} ${UPLOAD_DSYM_ONLY}
            fi

ERROR ITMS-90087解决脚本

APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"

# This script loops through the frameworks embedded in the application and
# removes unused architectures.
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"

EXTRACTED_ARCHS=()

for ARCH in $ARCHS
do
echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
done

echo "Merging extracted architectures: ${ARCHS}"
lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
rm "${EXTRACTED_ARCHS[@]}"

echo "Replacing original executable with thinned version"
rm "$FRAMEWORK_EXECUTABLE_PATH"
mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"

done

找到view所在controller

func responderViewController() -> UIViewController { 
  var responder: UIResponder! = nil 
  var next = self.superview 
  while next != nil { 
    responder = next?.next 
    if (responder!.isKind(of: UIViewController.self)){ 
      return (responder as! UIViewController) 
    } 
    next = next?.superview 
  } 
  return (responder as! UIViewController)
}

强制转屏

override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
   
UIDevice.current.setValue(UIInterfaceOrientation.landscapeRight.rawValue, forKey: "orientation")
UIApplication.shared.statusBarOrientation = .landscapeRight
}

判断系统SDK是否支持iOS10

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
#import 
@interface AppDelegate() 
@end
#endif

判断设备为手机还是iPad

if UI_USER_INTERFACE_IDIOM() == .Phone {
}
/*
    case Unspecified
    @available(iOS 3.2, *)
    case Phone // iPhone and iPod touch style UI
    @available(iOS 3.2, *)
    case Pad // iPad style UI
    @available(iOS 9.0, *)
    case TV // Apple TV style UI
    @available(iOS 9.0, *)
    case CarPlay // CarPlay style UI
*/

UISegmentedControl样式自定义

let sc: UISegmentedControl = UISegmentedControl(items: ["111","222"])
sc.layer.borderWidth = 0
        sc.layer.borderColor = UIColor.clearColor().CGColor
        sc.layer.cornerRadius = 0
        sc.selectedSegmentIndex = 0
        sc.setTitle("问答讨论", forSegmentAtIndex: 0)
        sc.setTitle("学习资料", forSegmentAtIndex: 1)
        
        sc.setBackgroundImage(UIImage(), forState: .Normal, barMetrics: .Default)
        sc.setBackgroundImage(UIImage(), forState: .Selected, barMetrics: .Default)
        sc.setBackgroundImage(UIImage(), forState: .Highlighted, barMetrics: .Default)

        sc.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor(hex: 0xa8a8a8)!, NSFontAttributeName: UIFont.systemFontOfSize(16)], forState: .Normal)
        sc.setTitleTextAttributes([NSForegroundColorAttributeName: CommenColor.Blue, NSFontAttributeName: UIFont.systemFontOfSize(16)], forState: .Selected)
        sc.setDividerImage(UIImage(named: "line_vertical"), forLeftSegmentState: .Normal, rightSegmentState: .Normal, barMetrics: .Default)

Swift懒加载

private lazy var tableView: UITableView = {
        let tb: UITableView = UITableView(frame: CGRectZero, style: .Plain)
        tb.delegate = self
        tb.dataSource = self
        
        tb.registerClass(LiveChatViewCell.self, forCellReuseIdentifier: "LiveChatViewCell")
        
        tb.separatorStyle = .None
        tb.tableFooterView = UIView()

        tb.estimatedRowHeight = 60
        tb.rowHeight = UITableViewAutomaticDimension
        return tb
    }()

UIView布局动画

// 告诉self.view约束需要更新
view.needsUpdateConstraints()
 // 调用此方法告诉self.view检测是否需要更新约束,若需要则更新,下面添加动画效果才起作用
view.updateConstraintsIfNeeded()
// 更新动画
UIView.animateWithDuration(duration) { 
    self.view.layoutIfNeeded()
}

UILabel富文本

let attach = NSTextAttachment()
attach.image = UIImage(named: "icon")
attach.bounds = CGRectMake(0, -2, 32, 18)
let attr = NSMutableAttributedString(string: "  标题")
attr.insertAttributedString(NSAttributedString(attachment: attach), atIndex: 0)

UILabel/UITextView加载html文本

NSString * htmlString = @"xxxx";  
NSAttributedString * attrStr = [[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUnicodeStringEncoding] 
options:@{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType } documentAttributes:nil error:nil];  
UILabel * myLabel = [[UILabel alloc] initWithFrame:self.view.bounds];  
myLabel.attributedText = attrStr;   

准确实时判断UIPanGesture的滑动方向

方法1:垂直/水平方向

        // 根据上次和本次移动的位置,算出一个速率的point
        let velocityPoint = pan.velocity(in: self)

        switch pan.state {
        case UIGestureRecognizerState.began:
            // 使用绝对值来判断移动的方向
            
            let x = fabs(velocityPoint.x)
            let y = fabs(velocityPoint.y)
            
            if x > y {
                self.panDirection = .horizontal
            } else {
                self.panDirection =.vertical
            }

方法2:向左/右

//滑动手势方法
-(void)actionOfPan: (UIPanGestureRecognizer*)pan
{
    CGFloat location = [pan locationInView:pan.view];
    static CGFloat lastX = 0; //静态局部变量,记录上一次位置
    switch (pan.state)
    {
        case UIGestureRecognizerStateBegan:
        {
            lastX = location.x; //开始时上一次滑动位置
        }
            break;
        case UIGestureRecognizerStateChanged:
        {
            CGFloat delta = location.x - lastX; //计算差值
            if (delta == 0) {
                return;
            }
            if (delta < 0) {
                NSLog(@"<----"); //向左
            }
            else{
                NSLog(@"---->"); //向右
            }
            lastX = location.x; //滑动时重新赋值上一次记录
        }
            break;
        case UIGestureRecognizerStateEnded:
        {
            lastX = 0; //重置
        }
            break;
        default:
        {
        }
            break;
    }
}

NSString中文编码

NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"test.txt" ofType:nil]];
NSStringEncoding encode = CFStringConvertEncodingToNSStringEncoding (kCFStringEncodingGB_18030_2000);
NSString *str = [[NSString alloc] initWithData:data encoding:encode];

点击缩放跳转动画

// point缩放动画
-(void)pagePositionScaleAnimationWithCell:(UICollectionViewCell*)cell view:(UIView*)view{
    view.center = cell.center;
    view.transform = CGAffineTransformMakeScale(cell.frame.size.width/view.frame.size.width, cell.frame.size.height/view.frame.size.height);

    CGFloat x = cell.center.x;
    CGFloat y = cell.frame.origin.y - self.collectionView.contentOffset.y + cell.frame.size.height/2 + 64;
    view.center = CGPointMake(x, y);
//    view.alpha = 0;
    
    [UIView animateWithDuration:0.3 animations:^{
        view.transform = CGAffineTransformMakeScale(1, 1);
        view.center = self.view.center;
//        view.alpha = 1;
    }];
}

// 水波动画
-(void)pageRippleAnimation:(UIView*)view{
    CATransition *transition = [CATransition animation];
    transition.duration = 0.8;
    transition.type = @"rippleEffect";
    // rippleEffect | cube | pageCurl | pageUnCurl | suckEffect | oglFlip | moveIn | fade | reveal
    [view.layer addAnimation:transition forKey:nil];
}

// center缩放动画
-(void)pageScaleAnimation:(UIView*)view{
    view.frame = self.view.frame;
    view.transform = CGAffineTransformMakeScale(0.1, 0.1);
    view.alpha = 0;
    
    [UIView animateWithDuration:0.3 animations:^{
        view.transform = CGAffineTransformMakeScale(1, 1);
        view.alpha = 1;
    }];
}

KVO实现数组个数改变的监听

//1. 定义数组
@property(nonatomic,strong)NSMutableArray *array;

//2. add observer --- @count为数组的属性
[self addObserver:self forKeyPath:@"array.@count" 
options:NSKeyValueObservingOptionNew context:nil];

//3. 改变数组的值,但注意不能用[array addObject: ] 或 [array insert]、removeObject等去改变数组的元素
//而是要改用:
[[self mutableArrayValueForKey:@"array"] addObject: xxx]; // KVC的方式获取数组

//4. 监听处理
-(void)observeValueForKeyPath:(NSString *)keyPath 
ofObject:(id)object change:(NSDictionary *)change 
context:(void *)context{
    if ([keyPath isEqualToString:@"pathArray.@count"]) {
        NSInteger count = [change[NSKeyValueChangeNewKey] integerValue];
        // do something...
    }
}

图片裁剪

// 从view渲染得到图片
-(UIImage *)renderImageFromCurrentView{
    // 渲染
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}
// 裁剪 - !!!rect记得 x 缩放比
-(UIImage *)clipImageFromOriginalImage: (UIImage*)orgImage
                                inRect: (CGRect)rect{

    rect.origin.x *= orgImage.scale;
    rect.origin.y *= orgImage.scale;
    rect.size.width *= orgImage.scale;
    rect.size.height *= orgImage.scale;
    
    UIImage *image = [UIImage imageWithCGImage:CGImageCreateWithImageInRect(orgImage.CGImage, rect)];
    return image;
}

计算出绘图轮廓矩形

-(CGRect)getOutlineRectOfCurrentPaths{ //轮廓矩形
    CGFloat xmin = CGRectGetMaxX(self.bounds);
    CGFloat ymin = CGRectGetMaxY(self.bounds);
    CGFloat xmax = 0;
    CGFloat ymax = 0;
    
    for (UIBezierPath *path in self.pathArray)
    {
        NSMutableArray *points = [NSMutableArray array];
        CGPathApply(path.CGPath, (__bridge void *)points, getPointsFromBezier);
        
        for (int i=0; i xmax) {
                xmax = x;
            }
            if (y < ymin) {
                ymin = y;
            }
            if (y > ymax) {
                ymax = y;
            }
        }
    }
    
    CGRect rect = CGRectMake(xmin, ymin, xmax-xmin, ymax-ymin);
    return rect;
}

获取UIBezierPath上的所有点

// 用法:
NSMutableArray *keyPoints = [NSMutableArray array];
CGPathApply(yourPath.CGPath, (__bridge void *)keyPoints, getPointsFromBezier);

// 现成方法
void getPointsFromBezier (void *info, const CGPathElement *element) {
    NSMutableArray *bezierPoints = (__bridge NSMutableArray *)info;
    
    CGPoint *points = element->points;
    CGPathElementType type = element->type;
    
    switch(type) {
        case kCGPathElementMoveToPoint: // contains 1 point
            [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]];
            break;
            
        case kCGPathElementAddLineToPoint: // contains 1 point
            [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]];
            break;
            
        case kCGPathElementAddQuadCurveToPoint: // contains 2 points
            [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]];
            [bezierPoints addObject:[NSValue valueWithCGPoint:points[1]]];
            break;
            
        case kCGPathElementAddCurveToPoint: // contains 3 points
            [bezierPoints addObject:[NSValue valueWithCGPoint:points[0]]];
            [bezierPoints addObject:[NSValue valueWithCGPoint:points[1]]];
            [bezierPoints addObject:[NSValue valueWithCGPoint:points[2]]];
            break;
            
        case kCGPathElementCloseSubpath: // contains no point
            break;
    }
}

半透明present出来的viewcontroller

MyViewController *vc = [MyViewController new];
vc.view.backgroundColor = [UIColor clearColor];
vc.modalPresentationStyle = UIModalPresentationOverCurrentContext; 
//尝试了这种模式下切换比较自然且会起到作用

隐藏tableview多余的分割线

//1 以前我是将left的margin设置得很大,目的是‘挤跑’多余的分割线
        tableView.separatorInset = UIEdgeInsetsMake(0, 100000, 0, 0);
// 然后在cell中,又设置回来,比如
        cell.separatorInset = UIEdgeInsetsMake(0, 10, 0, 0);

//2 后来发现有人这样做:【推荐】
        tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];

数组元素排序,NSString比较

需求:顺序/倒序 排序数组中的name属性

NSArray *array = @[
  @{
   @"name": @"02. 资源1"
},
@{
   @"name": @"03-1. 资源2"
},
@{
   @"name": @"02-1. 资源3"
}
];
-(NSArray*)sortedArrayWithArray: (NSArray*)array{
    NSComparator sort = ^(NSDictionary *obj1, NSDictionary *obj2){
        NSRange range = NSMakeRange(0, 6); //03-00 < 03-01
        // 顺序
        return [obj1[@"name"] compare:obj2[@"name"] options:NSNumericSearch range:range];
        // 倒序
//        return [obj2[@"name"] compare:obj1[@"name"] options:NSNumericSearch range:range];
    };
    NSArray *result = [array sortedArrayUsingComparator:sort];
    NSLog(@"字符串数组排序结果%@",result);
    return result;
}

输出:[
  @{
   @"name": @"02. 资源1"
},
@{
   @"name": @"02-1. 资源2"
},
@{
   @"name": @"03-1. 资源3"
}
];

NSString-boundingRect-较为精确

//包含换行、回车、空格等都会计算高度
NSDictionary *attributes = @{NSFontAttributeName: [UIFont systemFontOfSize:16]};
CGSize size = [str boundingRectWithSize:CGSizeMake(width, 0) 
options: NSStringDrawingTruncatesLastVisibleLine | 
NSStringDrawingUsesLineFragmentOrigin | 
NSStringDrawingUsesFontLeading 
attributes:attributes context:nil].size;

swift将图片重新渲染成新的颜色

extension UIView类的类方法

class func renderWithNewColor(imageName:String, color: UIColor) -> UIImage{
        let image = UIImage(named: imageName)!
        // @2x的图建议*2 不然会虚  一般的.png图就不用*1
        let size = CGSizeMake(image.size.width * 2, image.size.height * 2)
        
        UIGraphicsBeginImageContext(size)
        let context = UIGraphicsGetCurrentContext();
        CGContextTranslateCTM(context, 0, size.height);
        CGContextScaleCTM(context, 1.0, -1.0);
        CGContextSetBlendMode(context, .Normal);
        let rect = CGRectMake(0, 0, size.width, size.height);
        CGContextClipToMask(context, rect, image.CGImage);
        color.setFill()
        CGContextFillRect(context, rect);
        let newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return newImage;
    }

OC将UIView转UIImage

-(UIImage*)viewRenderToImage: (UIView*)view{
    CGSize size = view.bounds.size;
//第一个参数表示区域大小。第二个参数表示是否是非透明的。如果需要显示半透明效果,需要传NO,否则传YES。第三个参数就是屏幕密度了
    UIGraphicsBeginImageContextWithOptions(size, false, [UIScreen mainScreen].scale);
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return image;
}

OC单例

+(Manager*)sharedManager{
    static Manager* manager = nil;
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        manager = [Manager new];
    });
    return manager;
}

屏幕旋转 - 切记项目支持全屏时,该方法才起作用

【iOS】常用技术点小记1_第22张图片
配置.png
-(UIInterfaceOrientationMask)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskPortrait;
}

-(BOOL)shouldAutorotate{
    return true;
}

-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    return UIInterfaceOrientationPortrait;
}

选中/取消选中动画

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}

cell复用

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
    
    [self configureCell:cell forIndexPath:indexPath];
    
    return cell;
}

swift协议

@objc
protocol XXXDelegate :NSObjectProtocol {
    // 方法
    optional func xxx()
}
// 代理
    weak var delegate : XXXDelegate?

NavBar全透明

 nav.navigationBar.setBackgroundImage(UIImage(),forBarMetrics: UIBarMetrics.Default)
 nav.navigationBar.shadowImage = UIImage()
 nav.navigationBar.translucent = true

TabBar

// 添加子控件
    private func addViewController(childController: UIViewController, title: String) {
        let nav = NavgationViewController(rootViewController: childController)
        addChildViewController(nav)
        childController.tabBarItem.title = title
        childController.tabBarItem.image = UIImage(named: "tb_\(childViewControllers.count - 1)")
        childController.tabBarItem.selectedImage = UIImage(named: "tb_\(childViewControllers.count - 1)" + "_selected")
        // 设置tabBarItem的tag, 方便判断点击
        childController.tabBarItem.tag = childViewControllers.count-1
    }
    

// 点击profile的时候.判断是否登录. 如果没有登录, 需要跳转到登录界面, 反之则跳转到个人界面
    func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool
    {
        return true
    }

swift-单例

// 一句话实现单例(swift2.0)
static let sharedInstance = LoginHelper()
private override init() {} // 防止使用()初始化

swift-UIView自定义初始化方法

    convenience init(iconName: String, placeHolder: String, isLocation: Bool, isPhone: Bool, isSafe: Bool)
    {
        self.init(frame: CGRectZero)
        // 其他初始化设置
    }

你可能感兴趣的:(【iOS】常用技术点小记1)