Swift与OC

代码加限制Neon学习

2016-12-08  本文已影响228人  勇往直前888

背景介绍

  1. 用frame还是加限制?
    经过近4年的争论,采用AutoLayout,通过加限制来确定页面位置,这种观点已经占主流地位。frame还是会用,不过场合已经越来越少。

  2. 用IB还是纯代码写界面?iOS界传统的争论。纯代码写界面还是有很多人在坚持,特别是除了这些好用的第三方库。本人的观点和这篇文章一致iOS界面布局,代码还是IB?总有一款适合你这篇文章一致,采用混合模式,故事版、xib、代码哪个方便用哪个。这里关于ScrollView的处理值得提倡。

  3. 为了代码加限制,苹果还推出了VFL这种“象形文字”,苹果的这种创新精神是值得推崇的,不过是不是东西方文化差异,这种苹果自嗨的东西,怎么让人理解起来这么费劲?有这些功夫,不如思考将加限制那几个API改得好用一点。

  4. 用IB加限制比较简单,可是用代码加限制,苹果提供的API实在太难用,跟KVO的API属于同一难用的级别。真不知道苹果是怎么想的,难道不考虑“开发体验”了?
    幸亏还有强大的开源社区,在苹果的API上包了一层,让“开发体验”上升了几个层次。
    Object-C:Masonry
    Swift:SnapKit
    这个已经成为事实上的标准了,朋友圈中基本都用这两个中的一个

  5. 这次介绍的Neon也是代码加限制的,star数量虽然少一点,不过也达到3600了。思路完全不同,“开发体验”也是非常不错的

限制与特色

  1. 支持iOS9以上? 这个没有确认,只是从它的简介上推测。应该不是这样,因为看其源代码,并没有用到iOS9以上适用的API。
platform :ios, '9.0'
use_frameworks!
pod 'Neon'
  1. 代码是用Swfit写的,从介绍说,支持Swift3。现在XCode8默认都不支持iOS7了。所以,对于最低要求支持iOS7以及以下的APP,用这个估计会有困难,也不建议使用。所以,最低支持版本iOS8以上的可以尝试用这个。

  2. 从它的源代码看,并没有用AutoLayout那些难用的API,而是直接使用最原始的frame方式。从这一点推测出:不要在View的init或者ViewController的ViewDidLoad中用这个库。那个时候的frame是不可相信的,谁知道会出现什么事情?在View的layoutSubviews或者ViewController的viewWillLayoutSubviews和viewDidLayoutSubviews这两个函数中,frame应该确定了,在这里写应该好一点。

  3. 基本的思路是采用“场景法”或者是“案例法”,将常用的“场景”包装成方便使用的API函数。这种思路是非常巧妙的,让使用者在使用的时候不必关心是frame还AutoLayout,按照“人”的思维去思考就可以了。并且,这种语言跟“UI”等“文艺青年”是有共同语言的,没有“iOS技术”的痕迹。Masonry的make,其AutoLayout API的痕迹还是非常明显的,特别是右边距,下边距要用负数这一点,跟原生API一样难用,更不用说跟跟“UI”等“文艺青年”交流了。

  4. 从思路上来说,是“相对布局”思想,可是底层却是用绝对布局的frame来实现的。能将这两种看似矛盾的思想结合起来,作者的思路还是非常值得称道的。本来我看了其功能介绍,对其使用方式很认同,马上想到的是借用这种“场景法”思想,用Object-C也写一份,用在当前的工程中。API定义参考它的,具体实现还是用Masonry的make。看了源码之后,才知道自己的想法“脱裤子放屁---多此一举”。

  5. iOS和MacX都能用,是最基础的frame实现

#if os(iOS)
    import UIKit
#else
    import Cocoa
#endif

功能简介

  1. NeonAnchorable.swift表示单个view在父view中的位置

  2. NeonAlignable.swift表示同级两个view之间位置关系

  3. NeonGroupable.swift表示相同子view的均衡分布

Anchoring Views

  1. 居中
    public func anchorInCenter(width: CGFloat, height: CGFloat)

  2. 填充
    public func fillSuperview(left: CGFloat = 0, right: CGFloat = 0, top: CGFloat = 0, bottom: CGFloat = 0)

这里是padding的概念,都是正数。利用Swift默认参数特性,不给参数,就是完全充满。

  1. 靠近角落
public func anchorInCorner(_ corner: Corner, xPad: CGFloat, yPad: CGFloat, width: CGFloat, height: CGFloat)
// Corner定义
public enum Corner {
    case topLeft            
    case topRight
    case bottomLeft
    case bottomRight
}

同样这里的xPad、yPad都是正数

  1. 靠近边
public func anchorToEdge(_ edge: Edge, padding: CGFloat, width: CGFloat, height: CGFloat)
// Edge定义
public enum Edge {
    case top
    case left
    case bottom
    case right
}

同样这里的padding是正数

  1. 靠近边并且填充
public func anchorAndFillEdge(_ edge: Edge, xPad: CGFloat, yPad: CGFloat, otherSize: CGFloat)

otherSize是指填充时需要固定一边,width或者height。比如靠近上边top,那么就要指定高度height,宽度充满父view;这时otherSize就代表高度height
类型是CGFloat,不是CGSize,这个名字有可能带来误解,可以考虑取个更好一点的名字,比如widthOrHeight。

Align

  1. 与某个固定view的相对位置
public func align(_ align: Align, relativeTo sibling: Frameable, padding: CGFloat, width: CGFloat, height: CGFloat, offset: CGFloat = 0)
// Align定义
public enum Align {
    case toTheRightMatchingTop            
    case toTheRightMatchingBottom       
    case toTheRightCentered                   
    case toTheLeftMatchingTop
    case toTheLeftMatchingBottom
    case toTheLeftCentered
    case underMatchingLeft
    case underMatchingRight
    case underCentered
    case aboveMatchingLeft
    case aboveMatchingRight
    case aboveCentered
}
  • Frameable是自定义的协议,UIView和CALayer在NeonExtensions.swift已经遵循这个协议,可以正常使用
  • offset默认是0,在源码中是+,正负将带来不同的偏移方向
  1. 与某个固定view的相对位置,并且填充共同的父view
public func alignAndFill(align: Align, relativeTo sibling: Frameable, padding: CGFloat, offset: CGFloat = 0)
public func alignAndFillHeight(align: Align, relativeTo sibling: Frameable, padding: CGFloat, width: CGFloat, offset: CGFloat = 0)
public func alignAndFillWidth(align: Align, relativeTo sibling: Frameable, padding: CGFloat, height: CGFloat, offset: CGFloat = 0)
  1. 与两个view之间的相对位置,其中一个为主要参考
public func alignBetweenHorizontal(align: Align, primaryView: Frameable, secondaryView: Frameable, padding: CGFloat, height: CGFloat, offset: CGFloat = 0)
public func alignBetweenVertical(align: Align, primaryView: Frameable, secondaryView: Frameable, padding: CGFloat, width: CGFloat, offset: CGFloat = 0)

Align是相对于primaryView来定的

Grouping

  1. 居中均匀分布
public func groupInCenter(group: Group, views: [Frameable], padding: CGFloat, width: CGFloat, height: CGFloat)
// Group定义
public enum Group {
    case horizontal
    case vertical
}
  1. 靠近角落均匀分布
public func groupInCorner(group: Group, views: [Frameable], inCorner corner: Corner, padding: CGFloat, width: CGFloat, height: CGFloat)
  1. 靠近边均匀分布
public func groupAgainstEdge(group: Group, views: [Frameable], againstEdge edge: Edge, padding: CGFloat, width: CGFloat, height: CGFloat)
  1. 相对于某个固定view的均匀分布
public func groupAndAlign(group: Group, andAlign align: Align, views: [Frameable], relativeTo sibling: Frameable, padding: CGFloat, width: CGFloat, height: CGFloat) 
  1. 填充满并且均匀分布
public func groupAndFill(group: Group, views: [Frameable], padding: CGFloat)

UILabel由内容决定大小

// MARK: AutoHeight
//
///
/// `CGFloat` constant used to specify that you want the height to be automatically calculated
/// using `sizeToFit()`.
///
public let AutoHeight : CGFloat = -1
public let AutoWidth : CGFloat = -1

在需要自适应的地方,对应的width或者height参数输入AutoWidth或者AutoHeight

// MARK: UIView implementation of the Neon protocols.
//
extension View : Frameable, Anchorable, Alignable, Groupable {
    public var superFrame: CGRect {
        guard let superview = superview else {
            return CGRect.zero
        }

        return superview.frame
    }

    public func setDimensionAutomatically() {
        #if os(iOS)
            self.sizeToFit()
        #else
            self.autoresizesSubviews = true
            self.autoresizingMask = [.viewWidthSizable, .viewHeightSizable]
        #endif
    }
}
    public func anchorInCenter(width: CGFloat, height: CGFloat) {
        let xOrigin : CGFloat = (superFrame.width / 2.0) - (width / 2.0)
        let yOrigin : CGFloat = (superFrame.height / 2.0) - (height / 2.0)

        frame = CGRect(x: xOrigin, y: yOrigin, width: width, height: height)

        if height == AutoHeight {
            self.setDimensionAutomatically()
            self.anchorInCenter(width: width, height: self.height)
        }

        if width == AutoWidth {
            self.setDimensionAutomatically()
            self.anchorInCenter(width: self.width, height: height)
        }
    }

感悟

上一篇下一篇

猜你喜欢

热点阅读