[译]纯代码建立 UIView

[译]纯代码建立 UIViewios

翻译自:swift

https://medium.com/written-code/creating-uiviews-programmatically-in-swift-55f5d14502ae/设计模式

读完这篇文章,你能获得啥?

  • 了解 iOS 的屏幕构成
  • 视图关系
  • 什么时候适合使用代码方式构建视图
  • MVC 模式下,该如何组织代码
  • 自定义 UIView
  • 一个 Twitter iOS App 做为示例
  • 避免构建 Massive View Controller
  • 使用 PureLayout 构建约束

了解 iOS 屏幕

iOS App 由许多视图组成。视图的显示依赖四个值:x,y,width,height。bash

视图的基本构成

三种方式构建视图:StoryboardsNib files编码实现app

UIKit 包含许多标准组件,从简单的按钮,到复杂的表格。他们用处普遍,如 UILabel 对象绘制文本字符串,UIImageView 对象绘制图像。iview

视图能够被嵌入到其余视图,从而在视图之间产生父子视图关系,一个视图的父视图被称为superview,子视图被称为subviewide

视图关系

视图关系

如何组织你的视图关系着你的应用程序的视觉效果和事件行为。举一个例子,有两个视图,他们的父子关系决定了如何捕获事件及响应事件的顺序。相似的,当手机方向发生变化,视图的父子关系也决定了他们作如何修改。布局

什么时候使用代码形式构建视图

如下情景,一般都是适合使用代码构建视图的状况:

  • 动态布局
  • 视图须要实现一些效果,如圆角,阴影这类
  • 任何你感受使用 Storyboard 实现会复杂的时候

如何组织代码(MVC 模式)

Model-View-Controller 是最经常使用的设计模式。然而在 iOS App 开发过程当中,一般要面临一个问题:视图控制器经常变得过于庞大,修改和重构都很痛苦。因此 MVC 也被戏称为Massive View Controllerui

遵循此模式,咱们应该尽可能确保项目中的每一个类都是Controller、Model或者View。这能有效避免代码失控。咱们也能够建立其余的分组和类,但 App 的核心部分应该是这三种组成。编码

目录组织

准备

建立项目的时候,Xcode 会自动为咱们增长一个 storyboard。为了展现自定义视图,咱们干掉他先。

而后,建立两个文件:ProfileView 继承自 UIView,放到 View 分类中。ProfileViewController,继承自 UIViewController,放在 Controller 分类中。

Auto Layout

Auto Layout 决定了屏幕上视图的 frame。每一个视图都包含约束条件,经过这些条件来计算出视图的 width,height,x,y。直接编写 Auto Layout 代码并不容易,这里咱们使用 PureLayout,它提供了功能强大,使用友好的接口来帮助咱们编写 Auto Layout

首先添加 PureLayout 到你的项目中。我使用 CocoaPods 进行包管理,它依赖 Podfile 文件:

platform :ios, '8.0'
use_frameworks!
pod 'PureLayout', '~> 2.0.5'
复制代码

执行代码以安装依赖:

pod install
复制代码

这条命令将建立一个以.xcworkspace为扩展名的新的工程文件。如今,使用 Xcode 打开它。

Building custom classes

ProfileView.swift 文件当前是一个自定义 UIView 类的模板:

import UIKit
class ProfileView: UIView {

}
复制代码

咱们须要初始化它。初始化在 Swift 中是值得重视的事,你将在这里了解到更多关于它的事。如今,咱们只须要知道有一个主要的初始化器负责初始化当前类全部的属性。这是一个典型的实现:

import UIKit
import PureLayout

class ProfileView: UIView {
  var shouldSetupConstraints = true
    
  override init(frame: CGRect) {
    super.init(frame: frame)
  }

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
  }
    
  override func updateConstraints() {
    if(shouldSetupConstraints) {
      // AutoLayout constraints
      shouldSetupConstraints = false
    }
    super.updateConstraints()
  }
}
复制代码

给自定义的视图添加约束以前,咱们要覆盖 updateConstraints 方法。这个方法在运行期间可能会被调用屡次。为了不屡次添加约束给视图,咱们须要立一个 flag(shouldSetupContraints)来标示是否已经添加过约束。这个方法的最后,咱们也必须调用父类中的同名方法。(若是你在约束发生改变以前就调用,可能会 crash)。

Example

咱们来仿写一个 Twitter iOS app 的我的信息视图。在下面的图片中,能够看到顶部视图包括一个 Banner 图,用户头像,以及用户信息。而后下方是全部推展现在列表中以及tabbar。全部的 UIView 元素都拿紫色标注了起来。接下来咱们聚焦在 header view,橙色区域。

咱们先看主要的三部分。banner 和 用户头像使用 UIImageViews 进行展现,button 区域使用 UISegmentedControl。

首先,咱们定义三个元素在咱们的 ProfileView 类。bannerView,profileView 和 segmentedControl。

//ProfileView.swift
import UIKit
import PureLayout

class ProfileView: UIView {
  var shouldSetupConstraints = true
    
  var bannerView: UIImageView!
  var profileView: UIImageView!
  var segmentedControl: UISegmentedControl!
    
  override init(frame: CGRect){
    super.init(frame: frame)
    
  }
  
  ...
复制代码

在 init 方法中,咱们初始化这些视图元素的属性。背景颜色、边框颜色和其余基本的视觉属性。初始化他们的 frame 为 zero,AutoLayout 会自动调整大小和位置。

将这些视图元素添加为 ProfileView 的子视图使用 addSubview 方法。这个方法将被操做的视图放在其余子元素的最上面。代码以下:

//ProfileView.swift
import UIKit
import PureLayout

class ProfileView: UIView {
  var shouldSetupConstraints = true
    
  var bannerView: UIImageView!
  var profileView: UIImageView!
  var segmentedControl: UISegmentedControl!
    
  let screenSize = UIScreen.main.bounds
  
  override init(frame: CGRect){
    super.init(frame: frame)
        
    bannerView = UIImageView(frame: CGRect.zero)
    bannerView.backgroundColor = UIColor.gray
        
    bannerView.autoSetDimension(.height, toSize: screenSize.width / 3)
    
    self.addSubview(bannerView)
        
    profileView = UIImageView(frame: CGRect.zero)
    profileView.backgroundColor = UIColor.gray
    profileView.layer.borderColor = UIColor.white.cgColor
    profileView.layer.borderWidth = 1.0
    profileView.layer.cornerRadius = 5.0
        
    profileView.autoSetDimension(.width, toSize: 124.0)
    profileView.autoSetDimension(.height, toSize: 124.0)
    
    self.addSubview(profileView)
        
    segmentedControl = UISegmentedControl(items: ["Tweets", "Media", "Likes"])
        
    self.addSubview(segmentedControl)
  }
  ...
复制代码

使用 PureLayout 设置约束

布局约束

布局约束用来描述视图与其余视图的关系和属性。经过 NSLayoutConstraint 类来使用。

约束有如下几种:

  • 尺寸约束 - 如描述一个图片的宽为200px
  • 对齐约束 - 如描述一个 label 垂直居中在屏幕
  • 间隙约束 - 如描述两个元素之间的间隙

Attributes

PureLayout 定义了用来建立约束的视图属性,见图:

开始搞事吧!

屏幕上有三个巨星元素,咱们要把他们的位置大小调整如 Twitter 我的页面。

//ProfileView.swift
...
  override func updateConstraints() {
    if(shouldSetupConstraints) {

      let edgesInset: CGFloat = 10.0
      let centerOffset: CGFloat = 62.0
            
      bannerView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets.zero, excludingEdge: .bottom)
            
      profileView.autoPinEdge(toSuperviewEdge: .left, withInset: edgesInset)
      // 👇🏽 profileView.autoAlignAxis(.horizontal, toSameAxisOf: bannerView, withOffset: centerOffset)
      profileView.autoPinEdge(.bottom, to: .bottom, of: bannerView, withOffset: centerOffset)
            
      segmentedControl.autoPinEdge(toSuperviewEdge: .bottom, withInset: edgesInset)
      segmentedControl.autoPinEdge(toSuperviewEdge: .left, withInset: edgesInset)
      segmentedControl.autoPinEdge(toSuperviewEdge: .right, withInset: edgesInset)
            
      shouldSetupConstraints = false
    }
    
    super.updateConstraints()
...
}
复制代码

避免 MVC 成为 Massive View Controllers

下面进行完成 ProfileView 的最后一步。咱们须要在咱们的 Controller(ProfileViewController)中调用。Xcode 已经建立了一个 Controller 模板,并包含 viewDidLoad: 方法。这个方法会在 Controller 显示前进行回调。接下来咱们须要实例化咱们的 ProfileView 并展现它。

//ProfileViewController.swift
import UIKit

class ViewController: UIViewController {
  var profile: ProfileView!
  
  override func viewDidLoad() {
    super.viewDidLoad()

    profile = ProfileView(frame: CGRect.zero)
    self.view.addSubview(profile)
    
    // AutoLayout
    profile.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets.zero)
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
  }
}
复制代码

(最后:本文中的代码用于展现 Auto Layout。你须要补充其余代码才能使其成为一个完整的项目,加油!💃🏽👋)

相关文章
相关标签/搜索