uikit2 uikit3_uikit和装饰器设计模式第1部分

uikit2 uikit3

IOS中的设计模式 (DESIGN PATTERNS IN IOS)

Design patterns are general solutions to a commonly occurring problems, understanding them gives you more freedom and confidence of choosing solution to a specific problem.

设计模式是对常见问题的通用解决方案,了解它们可以使您更加自由和自信地选择特定问题的解决方案。

In this series of “Design patterns for iOS”, I will go through all common design patterns and show how you could use them in your UIKit or SwiftUI applications.

在“ iOS的设计模式 ”系列中我将介绍所有常见的设计模式,并说明如何在UIKit或SwiftUI应用程序中使用它们。

I will also adhere to swift’s best practices and point them out so some of you may find something useful besides the main topic.

我还将坚持swift的最佳做法,并指出这些最佳做法,以便你们中的一些人可能会发现除主要主题之外的有用内容。

In this part I’ve choose to have simple example as I want to focus more on the decorator design pattern itself and how we can abuse UIView behavior to make the usage even simpler.

在这一部分中,我选择有一个简单的示例,因为我想更多地关注装饰器设计模式本身,以及我们如何滥用UIView行为来简化用法。

Next part would have a bit more difficult example but the main principle of using Decorator pattern will remain the same.

下一部分将有一个更困难的示例,但使用Decorator模式的主要原理将保持不变。

So, let’s start.

所以,让我们开始吧。

“Decorator is a structural design pattern that lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.” — refactoring.guru

“ Decorator是一种结构设计模式,通过将这些对象放置在包含行为的特殊包装对象内,您可以将其附加到对象上。” — refactoring.guru

Now when we have this definition lets implement simple decorator that takes URL property and make UILabel clickable:

现在,当我们有了这个定义时,就可以实现带有URL属性并使UILabel可单击的简单装饰器:

import UIKit


class HyperlinkDecorator {
  var url: URL?
  var label: UILabel
  
  required init(label: UILabel) {
    self.label = label  
    label.isUserInteractionEnabled = true
    label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(hyperlinkedLabelTapped)))
  }
  
  @objc private func hyperlinkedLabelTapped() {
    if let url = url, UIApplication.shared.canOpenURL(url) {
      UIApplication.shared.open(url)
    }
  }
}

As you can see the implementation is straight forward. It takes UILabel, adds UITapGestureRecognizer and on tap it opens URL. We keep URL optional so it could be changed anytime.

如您所见,实现很简单。 它使用UILabel ,添加UITapGestureRecognizer ,然后点击打开URL 。 我们将URL保持为可选,因此可以随时更改。

Notice that we defined HyperlinkDecorator as a class, not struct. That’s because it has property with reference type. In this example we don’t really need to keep reference to UILabel, but let’s leave it there.

请注意,我们将HyperlinkDecorator定义为类,而不是struct。 这是因为它具有引用类型的属性。 在此示例中,我们实际上不需要保留对UILabel的引用, 但让它留在那里。

Now if we want to use our decorator we need to create and store it somewhere. UIGestureRecognizer does not retain it’s target, so if we don’t store the reference to hyperlinkDecorator it will get deallocated straight away.

现在,如果要使用装饰器,我们需要在装饰器中创建和存储它。 UIGestureRecognizer 不会保留其target ,因此,如果我们不存储对hyperlinkDecorator的引用,它将立即被释放。

class HyperlinkViewController: UIViewController {
  
  @IBOutlet weak var hyperLabel: UILabel!
  var hyperlinkDecorator: HyperlinkDecorator?
  
  override func viewDidLoad() {
    super.viewDidLoad()


    hyperlinkDecorator = HyperlinkDecorator(label: hyperLabel)
    hyperlinkDecorator?.url = URL(string: "https://medium.com")
  }
}

I don’t like to store references to objects that I don’t really use. If we have many decorators then we would have to create additional property for each of them and the code gets harder to read and maintain. We can also create an array or set of such objects and store them all there, so they will be deallocated together with HyperlinkViewController:

我不喜欢存储对我并不真正使用的对象的引用。 如果我们有很多装饰器,那么我们将不得不为每个装饰器创建附加属性,并且代码将变得更难以阅读和维护。 我们还可以创建此类对象的数组或集合并将其全部存储在其中,因此它们将与HyperlinkViewController一起释放:

class HyperlinkViewController: UIViewController {
  
  @IBOutlet weak var hyperLabel: UILabel!
  var decorators = [Any]()
  
  override func viewDidLoad() {
    super.viewDidLoad()


    let hyperlinkDecorator = HyperlinkDecorator(label: hyperLabel)
    hyperlinkDecorator.url = URL(string: "https://medium.com")
    decorators.append(hyperlinkDecorator)
  }
}

It may look better but I don’t like it either. That’s how I came to the next solution that I still use to get rid of such references by working with UIKit components.

它可能看起来更好,但我也不喜欢。 这就是我得出的下一个解决方案的方法,该解决方案仍通过与UIKit组件一起使用来摆脱此类引用。

We subclass HyperlinkDecorator from UIView, make it .zero size and add as a subview of UILabel, so it will live and die together with UILabel. We also set weak reference to UILabel to avoid retain cycle and memory leaks:

我们从UIView 继承 HyperlinkDecorator ,将其设置为.zero大小,并添加为UILabel的子视图,因此它将与UILabel一起生活和消亡。 我们还对UILabel设置了引用,以避免保留周期和内存泄漏

import UIKit


class HyperlinkDecorator: UIView {
  var url: URL?
  weak var label: UILabel?
  
  required init(label: UILabel) {
    self.label = label
    super.init(frame: .zero)
    label.isUserInteractionEnabled = true
    label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(hyperlinkedLabelTapped)))
    label.addSubview(self)
  }
  
  // It's required for UIView subclasses
  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }


  @objc private func hyperlinkedLabelTapped() {
    if let url = url, UIApplication.shared.canOpenURL(url) {
      UIApplication.shared.open(url)
    }
  }
}

The usage remains the same but we no longer need to keep the reference:

用法保持不变,但我们不再需要保留引用:

class HyperlinkViewController: UIViewController {  
  @IBOutlet weak var hyperLabel: UILabel!
  
  override func viewDidLoad() {
    super.viewDidLoad()


    let hyperlinkDecorator = HyperlinkDecorator(label: hyperLabel)
    hyperlinkDecorator.url = URL(string: "https://medium.com")
  }
}

That looks okay, but we can do it even better. Let’s make an extension for UILabel that hides this implementation and provides us convenient url property:

看起来还可以,但我们可以做得更好。 让我们对UILabel进行扩展,以隐藏此实现并为我们提供方便的url属性:

extension UILabel {    
  var url: URL? {
    get { 
      subview(type: HyperlinkDecorator.self)?.url 
    }    
    set {
      let decorator = subview(type: HyperlinkDecorator.self) ?? HyperlinkDecorator(label: self)
      decorator.url = newValue
    }
  }
}


extension UILabel {
  func subview<V: UIView>(type: V.Type) -> V? {
    return subviews.first(where: { $0 is V }) as? V
  }
}

There is generic function subview(type:) that searches for the subview of a specific type. I could use tag but I prefer this version as the code looks cleaner and we don’t need to worry about assigning unique tag value.

有一个通用函数 subview(type :)搜索特定类型的子视图。 我可以使用标签,但我更喜欢此版本,因为代码看起来更简洁,我们不必担心分配唯一的标签值。

And that’s how we can use it now:

这就是我们现在可以使用它的方式:

class HyperlinkViewController: UIViewController {  
  @IBOutlet weak var hyperLabel: UILabel!
  
  override func viewDidLoad() {
    super.viewDidLoad()
    hyperLabel.url = URL(string: "https://medium.com")
  }
}

Now it looks much better and I would actually stop here, but still can refactor UILabel extension and make it easier to add new properties and new decorators.

现在看起来好多了,我实际上会在这里停止,但是仍然可以重构UILabel扩展并使其更容易添加新属性和新装饰器。

That’s what I’m going to do:

那就是我要做的:

  • Move subview(type:) to the UIView extension

    subview(type :)移至UIView扩展

  • Add ViewDecorator protocol

    添加ViewDecorator协议

  • Add another generic function viewDecorator(type:) to the UIView extension that would search and create new decorator if it’s not found.

    将另一个通用函数viewDecorator(type :)添加到UIView扩展中,该扩展将搜索并创建新的装饰器(如果找不到)。

  • Conforms HyperlinkDecorator to ViewDecorator protocol

    使HyperlinkDecorator符合ViewDecorator协议

  • Update UILabel extension

    更新UILabel扩展

It seems not much changes, let’s do it.

似乎变化不大,让我们开始吧。

Moving function subview(type:) to UIView:

将函数subview(type :)移至UIView

import UIKit


extension UIView {
  func subview<V: UIView>(type: V.Type = V.self ) -> V? {
    return subviews.first(where: { $0 is V }) as? V
  }
}

View decorator should be subclass of UIView and should take any generic object of UIView or its subclass. Exact class of this generic object would be defined inside each view decorator.

视图装饰器应该是UIView的子类,并且应该采用UIView或其子类的任何常规对象。 此通用对象的确切类将在每个视图装饰器中定义。

import UIKit


protocol ViewDecorator: UIView {
  associatedtype View: UIView
  init(object: View)
}

Now let’s move code of searching and creation of new decorator from UILabel extension to separate generic function inside UIView extension:

现在,让我们将搜索和创建新装饰器的代码从UILabel扩展到UIView扩展内的单独泛型函数:

import UIKit


extension UIView {
  func viewDecorator<V: ViewDecorator>(type: V.Type = V.self) -> V {
    return subview(type: V.self) ?? V(object: self as! V.View)
  }
}

This function works exactly the same as before only now it’s generic. If you’re unable to understand how it works, please read more about generics.

此功能与以前完全一样,只是现在通用。 如果您无法理解其工作原理,请阅读有关泛型的更多信息

Conforming HyperlinkDecorator to ViewDecorator is simple, just add protocol conformation and replace init(label:) with init(object:)

HyperlinkDecorator整合到ViewDecorator很简单,只需添加协议构象并将init(label :)替换为init(object :)

Here’s complete code for HyperlinkDecorator:

这是HyperlinkDecorator的完整代码:

import UIKit


class HyperlinkDecorator: UIView, ViewDecorator {
  var url: URL?
  weak var label: UILabel?
  
  required init(object label: UILabel) {
    self.label = label
    super.init(frame: .zero)
    label.isUserInteractionEnabled = true
    label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(hyperlinkedLabelTapped)))
    label.addSubview(self)
  }
  
  // It's required to have for UIView subclasses
  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
  
  @objc private func hyperlinkedLabelTapped() {
    if let url = url, UIApplication.shared.canOpenURL(url) {
      UIApplication.shared.open(url)
    }
  }
}

And finally updated extension of UILabel, the reason for all of these steps:

最后更新了UILabel的扩展名,这是所有这些步骤的原因:

import UIKit


extension UILabel {
  var url: URL? {
    get { viewDecorator(type: HyperlinkDecorator.self).url }
    set { viewDecorator(type: HyperlinkDecorator.self).url = newValue }
  }
}

That’s it, that’s all. You can find complete example project here.

就是这样,仅此而已。 您可以在此处找到完整的示例项目。

In the next few parts I will show you few more useful extension based on this ViewDecorator that I’ve used in my projects.

在接下来的几部分中,我将向您展示一些基于我在项目中使用的ViewDecorator的有用扩展。

翻译自: https://medium.com/@vault087/uikit-and-decorator-design-pattern-part-1-6b7cef048831

uikit2 uikit3

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值