【iOS】常用技术点小记1

作者: 清無 | 来源:发表于2016-07-27 16:44 被阅读261次

    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

    CAGradientLayer

    debug UIView Hierarchy

    这将会显示所有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)
    

    代码查找约束

    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

    Product -> Scheme -> Edit Scheme

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

    All包含__cxa_throw异常捕获 只捕获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交互

    <script type="text/javascript">
          function test() {
            window.webkit.messageHandlers.Test.postMessage({body: '是否退出登录?'});
          }
        </script>
    
    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
    <script type="text/javascript"> 
            var u = navigator.userAgent; 
            var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端 
            var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端 
            if (isAndroid) {
                alert('这是Android'); 
            }
            if (isiOS) {
                alert('这是IOS'); 
            }
        </script>
    // 02
    <script type="text/javascript">
            if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
                //alert(navigator.userAgent);
                alert('这是IOS');
            } else if (/(Android)/i.test(navigator.userAgent)) {
                //alert(navigator.userAgent);
                alert('这是Android');
            } else {
                alert('这是PC');
            };
        </script>
    

    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

    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<UITouch>, with event: UIEvent?) {
            super.touchesBegan(touches, with: event)
            // 背景色
            backgroundColor = mcolors.gray.withAlphaComponent(0.2)
        }
        
        override func touchesEnded(_ touches: Set<UITouch>, 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问题

    删除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

    示例

    [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
        }
    
    然后设置`Main Interface`为某个storyboard

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

    SourceTree 操作1 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<T>(_ 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

    改变无效 .leading = 12+8

    关于storyboard的unwind

    //unwind一定是定义在当前页面vc的来源vc中,
    //如vc2是由vc1导航push进来的,那么该unwind方法就应该在vc1中,而不是v2中
    
    @IBAction func unwindForVC1(_ sender: UIStoryboardSegue){
            
    }
    

    MathJax数学表达式解析

    <script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML"></script>

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

    问题

    解决方法:

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

    xib或storyboard自动布局

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

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


    拉伸-压缩布局

    则布局设置优先级为:

    green view skin view

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

    //1. 当前view的touchesBegan方法
    override func touchesBegan(_ touches: Set<UITouch>, 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 <angled> include, use "quotes" instead.

    解决方法

    在`Build Settings`里设置

    Podfile编写

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

    StoryBoard上自动布局约束设置

    设置间距space约束 space to

    指定UIView的LayoutMargins

    // 默认为8dp - 定义了view的内边距
    view.layoutMargins = UIEdgeInsetsMake(20, 20, 20, 20)
    
    storyboard设置

    UIWebView和JS交互一种新方案:

    // 通过JS向body中新插入一个iframe标签,就会触发OC的webView shouldStartLoadWithRequest方法,
    // 从而传递来自html的参数达到和UIWebView交互的目的
    
    //JS方法
          <script language="javascript">
                function loadURL(url) {
                    var iFrame;
                    iFrame = document.createElement("iframe");
                    iFrame.setAttribute("src", url);
                    iFrame.setAttribute("style", "display:none;");
                    iFrame.setAttribute("height", "0px");
                    iFrame.setAttribute("width", "0px");
                    iFrame.setAttribute("frameborder", "0");
                    document.body.appendChild(iFrame);
                    // 发起请求后这个iFrame就没用了,所以把它从dom上移除掉
                    iFrame.parentNode.removeChild(iFrame);
                    iFrame = null;
                }
                function test() {
                    loadURL("xxx:xxx"); //要传递的参数
                }
            </script>
    
    // 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中添加下面的键值
    <key>ITSAppUsesNonExemptEncryption</key>
    <false/>
    

    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 <bugly_app_id> <bugly_app_key> <app_bundle_identifier> <app_version> <dSYM_src_dir> <bSYMBOL_dest_dir>
    #
    # #
    #
    # #注意:
    # # 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 <bugly_app_id> <bugly_app_key> <app_bundle_identifier> <app_version> <dSYM_src_dir> <bSYMBOL_dest_dir> [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 <UserNotifications/UserNotifications.h>
    @interface AppDelegate() <UNUserNotificationCenterDelegate>
    @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 = @"<html><body>xxxx</body></html>";  
    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<NSString *,id> *)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<points.count; i++)
            {
                CGFloat x = [points[i] CGPointValue].x;
                CGFloat y = [points[i] CGPointValue].y;
                if (x < xmin) {
                    xmin = x;
                }
                if (x > 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;
    }
    

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

    配置.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

        本文链接:https://www.haomeiwen.com/subject/bjiojttx.html