构建 iOS 界面:子类化 Views

maoqide 5年前
   <p>作者:Reda Lemeden, <a href="/misc/goto?guid=4959676841708517099" rel="nofollow,noindex">原文链接</a> ,原文日期:2016-04-28</p>    <p>译者: <a href="/misc/goto?guid=4959676841788082987" rel="nofollow,noindex">wiilen</a> ;校对: <a href="/misc/goto?guid=4959671213401204576" rel="nofollow,noindex">bestswifter</a> ;定稿: <a href="/misc/goto?guid=4958964417055952265" rel="nofollow,noindex">CMB</a></p>    <p>这篇文章是 <strong>构建 iOS 界面</strong> 系列的第四篇,本篇重点介绍:在没有原生系统编程经验的情况下,如何实现 iOS 的设计 —— 这对 Web 设计师及开发者们来说是极好的。这里也提供前面几篇文章: <a href="/misc/goto?guid=4959676841930822639" rel="nofollow,noindex">第一部分</a> - <a href="/misc/goto?guid=4959676842014164900" rel="nofollow,noindex">第二部分</a> - <a href="/misc/goto?guid=4959676842094258420" rel="nofollow,noindex">第三部分</a> 。</p>    <p>在 <a href="/misc/goto?guid=4959676842094258420" rel="nofollow,noindex">上一篇</a> 文章中,我们交替使用 Interface Builder 和 Swift,实现了一个自定义的按钮 —— 如果你一遍又一遍重复这个过程,除非你开发的是一个手电筒 App,UI 上只有一个按钮,不然这项工作很快就会让人心累。即便不谈无聊的重复工作,如果只更新一点功能上的细节,也需要对每一个按钮的实例进行修改,这种做法也是不靠谱的。下面我们将介绍一种更好的方法。</p>    <p><img src="https://simg.open-open.com/show/09b0341d0314e2c18b63b26b236a1d92.png"></p>    <h2>更恰当的方法</h2>    <p><a href="/misc/goto?guid=4959676842184600312" rel="nofollow,noindex">我们之前也提过</a> ,通过继承已有类的方法和属性,来创建一个新的类,这个过程被称为子类化。子类可以有选择地重写父类的行为,如果我们自定义 UIButton 的默认外观,子类化正是我们需要的方法。让我们看看具体应该怎么做。</p>    <p>如果你之前就下过 Swiftbot 工程项目,可以直接打开。你也可以 <a href="/misc/goto?guid=4959676842268785250" rel="nofollow,noindex">从 GitHub 上下载</a> 。</p>    <p>在 project navigator 右键点击父文件夹,选择 <em>New File…</em> 来添加一个新文件:</p>    <p><img src="https://simg.open-open.com/show/83f63d3a155a46c499e645374d8749d9.jpg"></p>    <p>选择 <em>iOS</em> 下的 <em>Source</em> ,然后从模版中选择 <em>Cocoa Touch Class</em> 。</p>    <p><img src="https://simg.open-open.com/show/8b2caae27a08e3a582dcf999496ee3df.jpg"></p>    <p>把类的命名为 <em>RoundedCornerButton</em> ,然后将 <em>Subclass of</em> 那一行设为 <em>UIButton</em> ,其他部分不动。在 Swift 中一般使用驼峰式命名法。为这个类取一个可以描述具体用途的名字,是一种好习惯。</p>    <p><img src="https://simg.open-open.com/show/6561651f52de2742026b1859b5f10d6e.jpg"></p>    <p>在刚生成的 Swift 文件中,删除所有的注释 —— 那些开头带有 // 的代码。最后代码看起来应该像下面这样:</p>    <pre>  <code class="language-swift">import UIKit    class RoundedCornerButton: UIButton { }  </code></pre>    <p>上面这段代码几乎是在 Swift 中创建一个子类所需要的最少代码。 <a href="/misc/goto?guid=4959676842347774205" rel="nofollow,noindex">第一篇文章</a> 中也介绍过, import UIKit 可以让我们访问那些定义在 UIKit 中的 API,这个例子中指的是 UIButton 。</p>    <p>只创建一个子类还不够,目前 Interface Builder 依然把我们的按钮当作 UIButton 。在我们增加代码之前,子类还只相当于父类的一个副本。</p>    <h2>类与实例</h2>    <p>我们 <a href="/misc/goto?guid=4959676842429553614" rel="nofollow,noindex">之前</a> 提到过,在 Swift 中,每个 control 都由 UIKit 中的某个类来表示。不过那时候我们还没有说明的是,这些类只定义了 view 对象最基础的外观和行为。换句话说,我们很少直接使用它们。</p>    <p>这也是实例发挥作用的地方。实例指的是遵循给定类的规范而构建的对象。在这个例子中,我们在 IB 中添加的按钮是 RoundedCornerButton 的实例。</p>    <p><img src="https://simg.open-open.com/show/d38b9c199c323da5e1f75a514252dc8f.jpg"></p>    <p>请注意 UIButton 类是如何在不具体设定值的情况下,声明每个按钮都需要有个 buttonType 属性的。按钮的实例可以自己决定 buttonType 。</p>    <p>现在,将我们的按钮改为这个新的子类的实例。</p>    <p>在 storyboard 中,选中这个按钮,点击右侧工具栏中的第三个(ID)图标。这会切换到 <em>Identity inspector</em> ,你可以在这里修改这个按钮实例独有的属性,比如它的类和 identifier。</p>    <p><img src="https://simg.open-open.com/show/9e586f356f1d7824608d4376a0b2a1bf.jpg"></p>    <p>在 <em>Class</em> 选项框中,输入之前创建的子类的名字。这会将这个按钮修改为 RoundedCornerButton 的实例,这样我们之前用代码创建的自定义行为,就都能应用到这个按钮上了。</p>    <p><img src="https://simg.open-open.com/show/2c26d71d7a11ea4f1765be2f4f42860e.jpg"></p>    <p>对子类的处理先到这里,由于现在我们不需要从 view controller 中直接访问这个按钮实例,让我们先把 <a href="/misc/goto?guid=4959676842501975334" rel="nofollow,noindex">之前创建</a> 的 outlet connection 移除。有几种方式可以做到这点,最简单的方法是:点击右侧面板最后一个图标,切换到 <em>Connections inspector</em> ,点击 <em>Referencing Outlets</em> 下 roundedCornerButton 旁边的 <em>x</em> 。</p>    <p><img src="https://simg.open-open.com/show/ebf50dd784fff9734828052e784ccff0.gif"></p>    <p>删除了 outlet 之后,我们需要移除 ViewController.swift 中对这个按钮的所有引用。删除该类声明部分的所有代码,最后代码看起来应该是下面这样:</p>    <pre>  <code class="language-swift">class ViewController: UIViewController { }  </code></pre>    <p>我们接下来没有什么需要用到 Interface Builder 的地方了。在我们回去继续处理子类之前,我们会给你一些启发,帮助你了解如何使用子类化来扩展 UIKit controls。</p>    <h2>子类化的常用策略</h2>    <p>当我们使用子类化时,最常见的任务 —— 同时也常是最有挑战性的任务 —— 是弄清楚哪些方法和属性需要重写,以及执行你自己添加的代码的顺序。如果什么地方出错了,一般是因为你对错误的方法进行了重写,或是代码执行顺序出了差错。</p>    <p>对 UIView 的子类来说,你常常想让 view 在加载完后立即应用自定义的样式。一般我们对下面这些方法进行重写:</p>    <ul>     <li>awakeFromNib() ,在 view 从 IB 中加载时被调用。</li>     <li>drawRect(_:) ,在 view 需要将自己绘制到屏幕上时被调用。</li>     <li>layoutSubviews() ,在 view 需要确定 subview 的大小与位置时被调用。</li>    </ul>    <p>当然还有更多其他方法,这篇文章中无法一一介绍。如果感到好奇,你可以通过阅读 <a href="/misc/goto?guid=4959676842595308048" rel="nofollow,noindex">官方的 UIView 文档</a> 来了解细节。</p>    <h2>重写</h2>    <p>为了在 Swift 中重写一个方法,我们在方法的开头添加 override 关键字,就像下面这样:</p>    <pre>  <code class="language-swift">class RoundedCornerButton: UIButton {    override func awakeFromNib() { }  }  </code></pre>    <p>我们重写了 awakeFromNib() 方法,在这个方法中加入我们的对图层的自定义,看上去这是一个不错的选择。如果你在做出这些改动之后运行你的 App,你会发现四个角依然是直角。这不出所料,因为我们移除了那些在 view controller 中设置实例图层的 cornerRadius 的代码。</p>    <p>在之前的代码中,为了设置圆角,我们是这样做的:</p>    <pre>  <code class="language-swift">roundedCornerButton.layer.cornerRadius = 4  </code></pre>    <p>由于现在我们直接在按钮的子类中进行修改,我们不需要再引用 roundedCornerButton :</p>    <pre>  <code class="language-swift">class RoundedCornerButton: UIButton {    override func awakeFromNib() {      layer.cornerRadius = 4    }  }  </code></pre>    <p>在这个例子中, layer 等同于 self.layer , self 是一个对该实例的引用。这意味着在 Swift 中你很少需要写 self ,除非编译器建议你这么做。</p>    <p>再次运行你的 App,现在我们的按钮上应该已经应用了圆角效果。</p>    <p><img src="https://simg.open-open.com/show/c2223c0afb3479f27f49661e76459885.jpg"></p>    <p>接下来的部分比较有趣:如果你在 IB 中按住 alt 来拖动并复制按钮,新的按钮会与原来的按钮完全相同,你不需要在 view controller 中改动新按钮的属性来达到这个效果。</p>    <p><img src="https://simg.open-open.com/show/9013ea2ed1936cbf8e6b2b5f4df6f28a.jpg"></p>    <p>不过这里也有个小问题。目前我们在 IB 中设置了按钮的背景颜色。这意味着如果以后需要修改所有按钮的颜色,我们需要在 IB 中手动修改每个按钮。</p>    <p>这个问题容易解决。我们只需要在子类中直接修改 roundedCornerButton 的背景颜色属性,这样所有的按钮的背景颜色都会被改为同一颜色:</p>    <pre>  <code class="language-swift">class RoundedCornerButton: UIButton {    override func awakeFromNib() {      layer.cornerRadius = 4      backgroundColor = UIColor(red: 0.75, green: 0.20, blue: 0.19, alpha: 1.0)    }  }  </code></pre>    <p>如果你运行 App,你会发现两个按钮的颜色都变成了 <em>Tall Poppy</em> 色 —— 一种由 <a href="/misc/goto?guid=4959676842672081675" rel="nofollow,noindex">Kromatic</a> 命名的颜色。上面说的方法也可以用于修改字体、字体颜色,甚至可以用于添加新的行为,比如展示某种进行中的状态。</p>    <p><img src="https://simg.open-open.com/show/8a065fbcc752c0f0fa019142fab47c90.jpg"></p>    <h2>结语</h2>    <p>子类化是一种构建自定义 iOS 界面的强大工具。你也可以不使用它,但如果你需要构建一个健全的、可扩展的、模块化的系统,它会为你提供许多帮助。</p>    <p>本文由 SwiftGG 翻译组翻译,已经获得作者翻译授权,最新文章请访问http://swift.gg。</p>    <p> </p>    <p>来自:http://swift.gg/2016/08/16/building-ios-interfaces-subclassing-views/</p>    <p> </p>    <p><span style="background:rgb(189, 8, 28) url("data:image/svg+xml; border-radius:2px; border:medium none; color:rgb(255, 255, 255); cursor:pointer; display:none; font:bold 11px/20px "Helvetica Neue",Helvetica,sans-serif; left:30px; opacity:0.85; padding:0px 4px 0px 0px; position:absolute; text-align:center; text-indent:20px; top:904px; width:auto; z-index:8675309">Save</span></p>