[译]iOS开发可复用框架入门(实例)

当你编写一个iOS应用程序时,你通常会什么都不想就导入Foundation或UIKit框架。 如果要使用字符串,日期,文件系统或线程,可以导入Foundation。如果要使用UITableViewController或UIAlertController,则可以导入UIKit。 如果你导入了UIKit,那么可以完全忽略Foundation,因为UIKit在后台会导入它。

关键是这些框架定义了你可以在代码中访问的所有方法,函数以及类。 如果忘记导入CoreBluetooth,你将无法连接到自定义蓝牙设备。 更重要的是,如果你不导入SpriteKit,那么肯定会无法将SpriteKit游戏推出市场。

这些框架都是Apple构建的。 开发人员是可以使用它们,这样我们就可以利用这些技术来让我们的iOS应用取得成功。

但什么是框架,我们为什么要使用它们?

框架是一组模块化的可复用代码,可用作更高级别软件的构建块。

使用框架的最佳理由是它们可以构建一次,并且可以重复使用无数次!

我们再来看看UIKit; UIKit由Apple开发,并不断更新。 这是一个可以被开发人员重复使用来创建新应用的项目。 我们可以在项目中使用它来定义超棒的功能,如动画,按钮(UI),转换和颜色(UI)。

我们知道Apple在iOS平台上广泛开发和使用框架,但我们呢?

创建框架是一项相对容易的任务,使用它们和让greenColor = UIColor.black一样简单。 让我们开始制作定制的Cocoa Touch Frameworks吧!

创建框架

我们将构建一个框架来模拟苹果iOS多年来一直使用的自定义状态警报。 我通常在音乐应用程序,Podcast应用程序和新闻应用程序中注意到它们。

这些自定义状态警报已经开始出现在各个地方,但没有一个是由Apple提供的公共框架来做的!

这些警报总体上相对简单。 它们由一个UIVisualEffectView(带有模糊效果)(作为背景),一个UIImage和一些文本组成。 创建一个框架来产生这些框架有多难? 其实并不糟糕!

开始

让我们打开Xcode并创建一个新项目。 选择iOS选项卡,向下滚动到框架和库,然后选择Cocoa Touch Framework。

我将命名为`AOModalStatus`,其中`AO`代表我的首字母缩写。

你会注意到GitHub中的这种模式,在这种模式中,人们会将框架上传到社区,可以让其他开发人员使用。
Apple的框架也遵循双字母前缀。 考虑UIKit中的UI和GLKit中的GL; 前者代表用户界面,后者代表OpenGL(或OpenGL ES),后者是GLKit构建的Open Graphics Library。

本教程使用Swift作为开发语言。 记得随时随地保存项目,让我们开始吧!

首先创建一个新文件。 在Source下,选择Cocoa Touch Class,然后从下拉列表中选择UIView。 我将它命名为AOModalStatusView。

一个XIB文件很像一个故事板,但它是用于单个视图而不是一组视图。

打开XIB文件并删除当前存在的视图。 在对象库中搜索Visual Effects View with Blur并将其拖入XIB。 在尺寸检查器中,从下拉列表中选择“Alignment Rectangle”,然后在宽度和高度中输入230个点。

现在就像故事板一样,我们将在视图中添加一些对象,这些对象将构成我们自定义的“AOModalStatusView”。

将Image View和两个Label拖动到视觉特效视图中,并将它们放置如下:

我设置图像的宽度和高度为130点。 图像水平居中,距离视觉效果视图顶部10个点。 第一个标签位于其下20个点,第二个标签位于下方5个点。 这两个标签都具有引导和尾随约束,以使它们到达视图的每一侧。 对于第二个标签,将属性检查器中的Lines选项设置为2,因为它是可能占用多行的副标题。

真绕口,但是一旦你设置了限制,最难的部分就完成了!

由于XIB是文件中的单个视图,因此应该将类分配给“File's Owner”部分。 故事板包含多个视图,所以我们不会在那里做同样的事情。

在XIB的文档大纲中(1)选择"File's Owner"。 (2)转到Identity Inspector,(3)并将Class部分分配给AOModalStatusView。


现在我们准备开始将XIB与代码合起来!

我们首先为视图中最重要的物体,图像和两个标签制作一些出口。 选择Assistant Editor,这样你可以同时查看.xib和.swift文件。

添加一个像这样的基本类:

class AOModalStatusView: UIView {
}

就像在故事板中一样,controle+点击并拖动我们几分钟前创建的类中的每个元素。 它应该看起来像这样:


如果在添加控件时遇到问题,请构建项目(command+ B)并重试。 如果这不起作用,请确保将File's Owner的类设置为“AOModalStatusView”,而不是视图的类。

现在我们可以回到标准编辑器,而不用管XIB文件。 打开AOModalStatusView.swift。

我们将创建public函数来修改这些属性,所以要隐藏对原生控件的访问。 在关键字weak之前添加关键字private,如下所示:

接着,在控件后添加以下代码:

// MARK: Set Up View
public override init(frame: CGRect) {
 // For use in code
  super.init(frame: frame)
  setUpView()
}

public required init?(coder aDecoder: NSCoder) {
   // For use in Interface Builder
   super.init(coder: aDecoder)
  setUpView()
}

这两个初始化器是Xcode在创建AOModalStatusView时就有了。 第一个可以直接在代码运行,而第二个需要在Interface Builder中使用。

你可能已经注意到setUpView()函数有问题......主要是因为我们还没有创建它。

但是在添加func setUpView()之前,我们需要添加一些支持变量。 将以下内容添加到控件下面的类里。

let nibName = "AOModalStatusView"
var contentView: UIView!

现在我们准备将setUpView()添加到文件中。 在我们编写的最后一个初始化代码之后添加以下代码。

private func setUpView() {
    let bundle = Bundle(for: type(of: self))
    let nib = UINib(nibName: self.nibName, bundle: bundle)
    self.contentView = nib.instantiate(withOwner: self, options: nil).first as! UIView
    addSubview(contentView)

    contentView.center = self.center
    contentView.autoresizingMask = []
    contentView.translatesAutoresizingMaskIntoConstraints = true

    headlineLabel.text = ""
    subheadLabel.text = ""
}

这里有相当的工作量,所以我会尽快完成。 首先,我们需要将变量contentView设置为我们创建的XIB文件中的视图。 这是通过访问框架的Bundle,NIB(引用已编译的XIB)以及最后的NIB视图来完成的。

接下来,我们要使用addSubview(contentView)将我们刚刚创建的contentView添加到此类的视图中。

然后,我们将contentView的框架设置为等于该类中父视图的边界。 之后,我们想告诉它,我们不让调整大小,因为这个视图具有特定的大小。 这就是为什么我们将autoresizingMask传递给一个空数组。

最后,我们希望将标签设置为空文本,这样我们的框架完全可定制。

在AOModalStatusView.swift中,将public关键字添加到类的开头,以便稍后直接引用它。

我们几乎完成了我们的框架! 在我们构建和使用我们的框架之前还有最后一件事。 请记住我们如何将我们的控件标记为private? 添加这三个函数,这样使用框架的用户可以在他们的代码中设置图像和标签控件。

// Provide functions to update view
public func set(image: UIImage) {
    self.statusImage.image = image
}
 public func set(headline text: String) {
    self.headlineLabel.text = text
}
public func set(subheading text: String) {
    self.subheadLabel.text = text
}

瞧! 我们有一个框架,不用花多长时间就可以把它整合在一起! 但它本身并没有太大的作用,所以我们来测试一下吧!

使用定制的框架

我们将创建一个新项目来测试框架。 在我看来,互联网有很多猫,但它绝对可以使用更多的小狗! 我要模拟一个快速的小狗照片应用程序。 我们称之为幼犬乐园!

Puppy Paradise的起点可以从我的GitHub下载。

继续并在Xcode中打开启动器项目。

开始

转到项目检查器的常规选项卡并向下滚动到它所说的“Embedded Binaries.”。点击+按钮,然后添加其他。 Finder窗口将会下降,在这里需要选择我们之前创建的AOModalStatus.xcodeproj框架。 你会注意到我们的框架已添加到Project Navigator中!

现在回到Embedded Binaries并再次点击+按钮。 这一次你会看到我们添加的框架! 点击它将其添加到项目的Embedded Binaries中。

快速构建项目(command+ B)以确保事情进展顺利! 不应该有任何错误。

现在我们有了自己的框架,我们需要确保Xcode实际上引用它,然后才能开始对它进行编码。 跳转到ViewController.swift文件并在import UIKit正下方import AOModalStatus。

ViewController.swift里有当用户在查看图片时点击“保存”时执行的操作。 在saveTapped(_ :)函数内部添加presentModalStatusView()。 现在,让我们将presentModalStatusView()函数添加到该类中,以便从编译器独立出来。

func presentModalStatusView() {
    let modalView = AOModalStatusView(frame: self.view.bounds)
    let downloadImage = UIImage(named: "download") ?? UIImage()
    modalView.set(image: downloadImage)
    modalView.set(headline: "Downloading")
    view.addSubview(modalView)
}

这里做了一些事情。 首先,我们初始化一个AOModalStatusView并将其命名为modalView。 然后我们使用存储在资源中的“download.png”图像创建了一个UIImage。 然后,我们将图像和一些文本设置到视图中,并将其作为子视图添加到ViewController。

在模拟器上构建并运行应用程序,并尝试保存照片! 它的工作原理! 除了少数几个方面似乎缺乏。 例如,我们无法关闭它。 也没有圆角。 最重要的是,Apple使用了微妙的动画来显示和删除它们的模态状态视图。

那么,事实证明,这些并不难实现! 由于我们导入了框架的项目而不仅仅是编译的框架,所以我们可以对其进行更改,并且每次构建PuppyParadise时Xcode都会编译它。

Timer

点击框架项目旁边的下拉菜单,然后导航到刚刚创建的AOModalStatusView.swift文件。 第一步是在给定的几秒钟后使视图自行关闭。

在contentView变量的声明之后添加var timer: Timer?。 然后添加下面的代码:

public override func didMoveToSuperview() {
    // Add a timer to remove the view
        self.timer = Timer.scheduledTimer(
            timeInterval: TimeInterval(3.0),
            target: self,
            selector: #selector(self.removeSelf),
            userInfo: nil,
            repeats: false)
}
@objc private func removeSelf() {
    self.removeFromSuperview()
}

这是一段简单的代码:“在三秒钟后消除这个视图。”

圆角

接下来是圆角,这大大降低了AOModalStatusView的清晰度。 将以下代码添加到该类中:

// Allow view to control itself
public override func layoutSubviews() {
    // Rounded corners
    self.layoutIfNeeded()
    self.contentView.layer.masksToBounds = true
    self.contentView.clipsToBounds = true
    self.contentView.layer.cornerRadius = 10
}

这是可以用来创建圆角的典型代码。 需要提及的重要部分是,这是在子视图(AKA contentView)已经布置并且我们将clipsToBounds设置为true之后发生的,因此contentView中包含的子视图不能从圆角后面滑出。

现在运行它! 当我们去保存我们可爱的小狗照片几秒钟后消失,我们有圆角。

现在还有一件事要做,让它真的“弹出来!”让我们添加一些动画。

动画

可以理解的是,很多人都害怕在Swift中使用动画,但我会告诉你如何做一些如此简单而有效的事情,以便在此之后你可能会查看动画教程!

将didMoveToSuperview和removeSelf函数替换为以下内容:

public override func didMoveToSuperview() {
    // Fade in when added to superview
    // Then add a timer to remove the view
  self.contentView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
    UIView.animate(withDuration: 0.15, animations: {
        self.contentView.alpha = 1.0
        self.contentView.transform = CGAffineTransform.identity
    }) { _ in
        self.timer = Timer.scheduledTimer(
            timeInterval: TimeInterval(3.0),
            target: self,
            selector: #selector(self.removeSelf),
            userInfo: nil,
            repeats: false)
    }
}
@objc private func removeSelf() {
    // Animate removal of view
    UIView.animate(
        withDuration: 0.15,
        animations: {
        self.contentView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
        self.contentView.alpha = 0.0
    }) { _ in
        self.removeFromSuperview()
    }
}

然后将contentView.alpha = 0.0添加到setUpView的末尾。

这里做的事情并不复杂。 在didMoveToSuperview中,我们正在将contentView转换为宽度和高度的一半。 然后我们用UIView.animate(withDuration)运行一个动画。 在这里面我们将动画设置为CGAffineTransform.identity,这是一种奇特的方式,说“它通常应该看起来如何(AKA不是它的一半大小)。”我们还将动画中的alpha值设置回1.0。 这会告诉contentView在其动画过程中淡入时会从其大小的一半增长。 动画完成后,我们立即设置最初创建的计时器。

在removeSelf 中,我们正在做相反的事情。 在动画过程中,我们希望alpha值回落到0.0(或透明),转换再次使contentView成为其平常大小的一半。 动画完成后,我们可以完全消除视图。

我想指出,所有这些代码都发生在框架内部,并且当我们在项目中使用它时,我们仍然只需要一些通用的代码行。

现在让该项目运行并尝试“保存”该照片。 你现在应该有一个工作框架和项目来测试该框架。 除此之外,你可以在另一个项目中重用此框架,甚至可以将代码推送到GitHub以供其他人开始使用!

你可以从Github上下载最终版本的Puppy Paradise和AOModalStatusView

它是如何做的? 实现它可能有很多工作要做,但是每次你写一个新的框架,它只会变得更快,更容易!

原文:Getting Started with Reusable Frameworks for iOS Development

版权声明:著作权归作者所有。

相关推荐

IndexDB快速入门

IndexDB是适用于浏览器的文档数据库,它有以下特点:兼容所有现代的浏览器支持事务以及版本控制支持无限数量的数据。很多浏览器会限定localStorage或者sessionStorage的存储空间为2M到10MIndexDB是异步的API,它不会阻塞浏览器UI的渲染下面介绍下它的使用。安装依赖包idb需要把idb的js库添加到依赖。有几种方式添加idb到依赖。yarnyarn add&

iOS动态改变App Icon

从iOS10.3开始,苹果就支持了开发者通过编程来动态改变app的icon。这里简单介绍下如何动态改变app icon。首先要了解一下三个属性/api:var supportsAlternateIcons: Bool { get } var alternateIconName: String? { 

Vue实例里this的使用

要理解Vue实例里this的使用,首先要理解this在JavaScript里的用法,可以参考理解JavaScript普通函数以及箭头函数里使用的this。这是vue文档里的原话:All lifecycle hooks are called with their 'this' context pointing 

SpringBoot入门示例

创建Maven POM文件在pom.xml添加内容如下:<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="ht

[译]iOS11安全区布局指南(Safe Area Layout Guide)

Apple在iOS7为UIViewController新增了topLayoutGuide和bottomLayoutGuide属性。它们可以让你创建约束以避免内容被UIKit的横条,如状态、导航或标签栏覆盖。在iOS 11这些布局指南被废弃,并被单一的安全区布局指南代替。顶部和底部布局指南——概述这是使用顶部和底部布局指南在导航控制器和标签栏插入视图控制器的示例:绿色的内容视图相对底部锚点

[译]支持iPhone X

苹果发布了顶部带有“凹口”,底部是主屏幕指示器的iPhone X,WWDC 2017公布SDK的一些修改背后的原因就更清晰了。安全区域布局指南(Safe area layout guides)可能有所帮助,但对于表/集合视图和搜索栏还是有一些问题。安全区域布局指南(Safe Area Layout Guide)苹果在i