SwiftGGTeam / translation

translation work flow
84 stars 13 forks source link

【中】iOS: A Beautiful Way of Styling IBOutlets in Swift #56

Open mmoaay opened 6 years ago

mmoaay commented 6 years ago

http://szulctomasz.com/ios-a-beautiful-way-of-styling-iboutlets-in-swift/

mmoaay commented 6 years ago

title: "iOS:在 Swift 中优雅的配置 IBOutlets 的样式" date: tags: [Swift] categories: [Tomasz Szulc] permalink: ios-a-beautiful-way-of-styling-iboutlets-in-swift keywords: custom_title: description:


原文链接=http://szulctomasz.com/ios-a-beautiful-way-of-styling-iboutlets-in-swift/ 作者=Tomasz Szulc 原文日期=2016-06-22 译者=wiilen 校对= 定稿=

最近我正纠结于如何配置视图的样式。通常我会尽量在 storyboard 中完成相关工作,然后创建 IBOutlet 引用,并在 view controllers 中完成剩下的工作 —— 这并不是最理想的方法,我也知道。

现在的问题会更复杂一些。我在 storyboards 中使用大量的占位视图,这些视图将会持有更复杂的自定义控件。这些类型的控件在 storyboard 中只有占位引用,难以在 storyboard 中进行完全的自定义。我的意思是,你也可以使用 @IBDesignable,但在一些情况下你将会重复定义内部视图的属性。我十分推崇将控件内的视图暴露出来并在外部进行自定义的做法 —— 但这篇文章中将不会涉及这些。

因此,我在思考如何在不扰乱 view controller 中的代码与逻辑的情况下配置这些控件。经过一番 Google 我发现了 @NatashaTheRobot 写的文章:iOS:在 Swift 中优雅的配置 IBOutlets 的样式(你可以先读读这篇文章)(译者注:与本文的标题确实是一样的)。

原文中示例代码如下:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var myLabel: UILabel! {
        didSet {
            myLabel.textColor = UIColor.purpleColor()
        }
    }

    @IBOutlet weak var myOtherLabel: UILabel! {
        didSet {
            myOtherLabel.textColor = UIColor.yellowColor()
        }
    }

    @IBOutlet weak var myButton: UIButton! {
        didSet {
            myButton.tintColor = UIColor.magentaColor()
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

她所提出的方法确实是挺优雅的,我选择使用这种方法试试。然后我意识到我需要对控件进行大量的自定义,于是我马上就不喜欢这种做法了,甚至有些讨厌它,因为我发现 view controller 会变得越来越大。

使用这种方法对 outlets 进行样式配置需要 2 + n 行代码,n 指的是样式代码的行数。为了可读性你可能需要添加一些空行。使用 10 个属性进行样式配置就需要添加大量的额外代码,这些我们也许可以避免。

我不是说这种方法不好,你一定不能这么做,我不是这个意思。实际上我也喜欢这种方法,但对我目前的项目来说这不是最好的解决方法。

另一种优雅(?)的方法

举一个更简单的例子。我想要配置一些 lable、一个 button 与一个 view,你也可以想象一些与实际更贴近的例子,就像我在上面提到的一样 —— 每个控件都是自定义的,你需要在某个地方配置样式 —— 你也可以通过创建一些 DRY 方法来避免重复的配置代码 —— 我希望你写了这样的代码 :)

class ManyLabelsViewController: UIViewController {
    @IBOutlet private var label1: UILabel!
    @IBOutlet private var label2: UILabel!
    @IBOutlet private var label3: UILabel!
    @IBOutlet private var label4: UILabel!
    @IBOutlet private var label5: UILabel!
    @IBOutlet private var label6: UILabel!
    @IBOutlet private var button1: UIButton!
    @IBOutlet private var view1: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // 一些其他代码...
    }
}

让我们来添加一些样式。

import UIKit

class ManyLabelsViewController: UIViewController {
    @IBOutlet private var label1: UILabel! {
        didSet {
            label1.textColor = UIColor.redColor()
            label1.font = UIFont.systemFontOfSize(20)
            label1.backgroundColor = UIColor.blueColor()
        }
    }

    @IBOutlet private var label2: UILabel! {
        didSet {
            label2.layer.borderColor = UIColor.yellowColor().CGColor
            label2.layer.borderWidth = 1
            label2.backgroundColor = UIColor.blueColor()
            label2.clipsToBounds = true
        }
    }

    @IBOutlet private var label3: UILabel! {
        didSet {
            label3.textColor = UIColor.purpleColor()
        }
    }

    @IBOutlet private var label4: UILabel! {
        didSet {
            label4.textAlignment = .Center
            label4.textColor = UIColor.grayColor()
        }
    }

    @IBOutlet private var label5: UILabel! {
        didSet {
            label5.backgroundColor = UIColor.greenColor()
            label5.font = UIFont.boldSystemFontOfSize(28)
        }
    }

    @IBOutlet private var label6: UILabel! {
        didSet {
            label6.lineBreakMode = .ByClipping
            label6.numberOfLines = 0
        }
    }

    @IBOutlet private var button1: UIButton! {
        didSet {
            button1.layer.borderWidth = 1
            button1.layer.borderColor = UIColor.blueColor().CGColor
        }
    }

    @IBOutlet private var view1: UIView! {
        didSet {
            view1.backgroundColor = UIColor.cyanColor()
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // 一些其他代码...
    }
}

Wow... view controller 中已经有 65 行代码,而现在还没有任何逻辑代码。在我们开始配置样式前就有 17 行。这看起来可不像一个优雅的方法。你可以看见那些属性声明吗?当然,它们清楚可见。

当你想要创建一些只用来配置样式的 outlets 时,这将变得更糟。如果没有这些自定义的样式,你就不需要创建这样的 outlet 了。

有比这更好的解决办法吗?有的。是否存在通用的解决方案?不一定,这种方法适用于我的项目,比起上面所说的办法,我得到了更好的结果。

既然我们总要使用 storyboard,为什么不创建一些样式对象来帮助我们做这些脏活?要创建这个对象,只需要拖动 Object 对象到 storyboard 的 view controller 中,并创建以 ViewControllerStyle 为后缀的类,连接 outlets 并进行样式配置。你也可以在许多对象中创建对特定的视图/控件的引用,而不仅是 view controller。

Style 对象

这样做之后,样式代码可以都放到 ManyLabelsViewControllerStyle 类中。

class ManyLabelsViewControllerStyle: NSObject {

    @IBOutlet private weak var label1: UILabel!
    @IBOutlet private weak var label2: UILabel!
    @IBOutlet private weak var label3: UILabel!
    @IBOutlet private weak var label4: UILabel!
    @IBOutlet private weak var label5: UILabel!
    @IBOutlet private weak var label6: UILabel!
    @IBOutlet private weak var button1: UIButton!
    @IBOutlet private weak var view1: UIView!

    func style() {
        styleLabel1()
        styleLabel2()
        styleLabel3()
        styleLabel4()
        styleLabel5()
        styleLabel6()
        styleButton1()
        styleView1()
    }

    private func styleLabel1() {
        label1.textColor = UIColor.redColor()
        label1.font = UIFont.systemFontOfSize(20)
        label1.backgroundColor = UIColor.blueColor()
    }

    private func styleLabel2() {
        label2.layer.borderColor = UIColor.yellowColor().CGColor
        label2.layer.borderWidth = 1
        label2.backgroundColor = UIColor.blueColor()
        label2.clipsToBounds = true
    }

    private func styleLabel3() {
        label3.textColor = UIColor.purpleColor()
    }

    private func styleLabel4() {
        label4.textAlignment = .Center
        label4.textColor = UIColor.grayColor()
    }

    private func styleLabel5() {
        label5.backgroundColor = UIColor.greenColor()
        label5.font = UIFont.boldSystemFontOfSize(28)
    }

    private func styleLabel6() {
        label6.lineBreakMode = .ByClipping
        label6.numberOfLines = 0
    }

    private func styleButton1() {
        button1.layer.borderWidth = 1
        button1.layer.borderColor = UIColor.blueColor().CGColor
    }

    private func styleView1() {
        view1.backgroundColor = UIColor.cyanColor()
    }
}

当我们不需要对 view controller 中的这些控件进行引用时,可以移除它们。或者把它们留着也行,取决于你的需求。

某些时候你需要调用 style() 方法,所以最后你只需要在 view controller 与该对象间建立 outlet,并在 viewDidLoad 中调用 style()

最后 view controller 看起来像下面这样。

class ManyLabelsViewController: UIViewController {

    // 保留那些你需要调用的控件...

    @IBOutlet private var style: ManyLabelsViewControllerStyle!

    override func viewDidLoad() {
        super.viewDidLoad()
        style.style()
        // 一些其他代码...
    }
}

2016 年 6 月 30 日更新

你也可以在 样式 对象中使用 didSet,在 outlets 设置完成后让该对象来配置。

class ManyLabelsViewControllerStyle: NSObject {

    @IBOutlet private weak var label1: UILabel! {
      didSet {
        label1.textColor = UIColor.redColor()
        label1.font = UIFont.systemFontOfSize(20)
        label1.backgroundColor = UIColor.blueColor()
      }
    }

    @IBOutlet private weak var label2: UILabel! {
      didSet {
        label2.layer.borderColor = UIColor.yellowColor().CGColor
        label2.layer.borderWidth = 1
        label2.backgroundColor = UIColor.blueColor()
        label2.clipsToBounds = true
      }
    }

    @IBOutlet private weak var label3: UILabel! {
      didSet {
        label3.textColor = UIColor.purpleColor()
      }
    }

    @IBOutlet private weak var label4: UILabel! {
      didSet {
        label4.textAlignment = .Center
        label4.textColor = UIColor.grayColor()
      }
    }

    @IBOutlet private weak var label5: UILabel! {
      didSet {
        label5.backgroundColor = UIColor.greenColor()
        label5.font = UIFont.boldSystemFontOfSize(28)
      }
    }

    @IBOutlet private weak var label6: UILabel! {
      didSet {
        label6.lineBreakMode = .ByClipping
        label6.numberOfLines = 0
      }
    }
    @IBOutlet private weak var button1: UIButton! {
      didSet {
        button1.layer.borderWidth = 1
        button1.layer.borderColor = UIColor.blueColor().CGColor
      }
    }
    @IBOutlet private weak var view1: UIView! {
      didSet {
        view1.backgroundColor = UIColor.cyanColor()
      }
    }
}

你的 view controller 看起来会像下面这样:

class ManyLabelsViewController: UIViewController {

    // 保留那些你需要调用的控件...

    @IBOutlet private var style: ManyLabelsViewControllerStyle!
    // 不需要调用其他方法。
}
Yousanflics commented 6 years ago

文章原链接404了 orz