Tag: swift

iOS中的轮播通知

作为Dolap.com,我们始终致力于提供最佳的移动用户体验。 上个月,我们试图找到一种方法来向用户显示项目列表,而不会强迫他们打开应用程序。 我们最终决定在iOS 10中引入富用户通知的自定义实现。 方法如下: 首先,如果要创建自定义通知,则应使用“通知内容扩展”。 我创建了一个名为carousel的扩展,然后将显示一个弹出窗口。 激活它以创建扩展方案。 之后,您将看到您的新方案。 要进行调试,您应该选择轮播方案⚠️ 在左侧,您将有一个名为carousel的新文件,其中包含ViewController,storyboard和info.plist 您的ViewController直接从UNNotificationContentExtension协议扩展。 它符合didReceive(_ notification:UNNotification)方法 通知扩展后,didReceive方法将被调用。 如果您想显示一些数据,那就是地方place 让我们结帐info.plist🤔 我们对这里的主要兴趣是类别标识符。 在发送推送通知时,应正确设置通知内容的类别标识符。 否则,由于标识符错误,您的自定义通知将无法正常工作 ContentSizeRatio的默认值为1。您可以根据需要更改它,但是它有责任使UI适应通知大小。 此外; 如果将UNNotificationExtensionDefaultContentHidden添加到NSExtensionAttributes并将其设置为YES,则将隐藏通知内容,仅显示您的UI 注意:系统会阻止在屏幕上向视图控制器传递触摸事件。 不要在界面中安装手势识别器或依赖触摸事件。 因此,☝️i添加了下一个和上一个动作按钮。 让我们谈谈他们。 在UNNotificationContentExtension协议中有一种处理操作的可选方法 注册自定义通知时,还应该创建操作并确定其actionIdentifiers。 因为如果您想处理每个操作单击,则将需要该标识符。 我们遵循didReceive:completionHandler方法并处理动作。 请注意完成,因为它采用responseOption作为参数。 您具有关闭,dismissAndForwardAction和doNotDismiss选项。 因此,如果您不想打开应用程序,则应使用doNotDismiss选项👊 总而言之,我们学到了; 如何创建通知内容扩展 如何使用指定的类别标识符进行注册 如何添加动作并处理其点击 感谢您阅读🤓寻找源代码? 🚀

在Swift中使用镜头进行嵌套依赖项修改

我个人喜欢KrzysztofZabłocki在他的文章中描述的模式。 这是一种简单的纯解决方案,它在层次结构中构建了依赖关系。 让我们来看一个例子: 想象一下这样一种情况,我们有提供用户年龄并具有Validator对象作为依赖项的UserProvider 。 在ViewModel某个地方,我们有一个反应性信号,通知用户年龄是否合适,例如: 现在,在测试中,我们有一个默认的验证实现,如果用户年满18岁,则返回true。 但是出于某种原因(例如测试),我们希望修改此验证以始终返回false ,这意味着由于年龄原因,可能不允许用户查看内容。 请注意, Validator对象嵌套在UserProvider中, UserProvider也嵌套在整个AppDependencies对象中。 这意味着我们必须编写很多代码,例如: 这也意味着您必须使每个struct属性var看起来很奇怪,因为我们不应该突变那些依赖结构。 现在想象一下, UserValidator比这三个闭包复杂得多,并且包含的​​内容更多。 这意味着很多样板代码,这当然是不需要的,尤其是在应该快速进行的测试中。 解? 镜片。 斯威夫特镜头是布兰登威廉姆斯推出的结构。 您可以找到许多文章和视频来描述确切的镜头,但基本上,这些结构允许通过返回新实例来view并将某些特定属性set为不可变类型。 看到与UserValidator相同的修改,但是这次我们将使用镜头: 现在,我们保存与UserValidator执行的验证一样多的代码行。 但这是唯一的收获吗? 更改两个特定的(可能不同的)依赖项属性怎么样? 这就是镜头真正闪耀的地方! 请注意,我们修改了两个不同的属性,一个来自UserProvider ,另一个来自UserValidator. 这意味着您的应用程序中不再有任何样板代码,对于这些结构,您可以随意使用let而不是var🙂 但是等等,我错过了什么吗? 那些镜片在哪里? 我强烈建议您使用Sourcery工具(KrzysztofZabłocki的另一个工具)来自动生成镜头(并构造初始化器)。 您可以在Sourcery github存储库中找到模板。 通过将透镜与Sourcery结合使用,定义新的依赖关系并不重要,您只需要重新运行Sourcery脚本,一切都会自动更新。 这个故事与依赖注入的优缺点无关,但是它可能鼓励您在应用程序中使用此模式而不会受到伤害。 它使测试易于执行,并且确保快速进行。 从代码构造视图时,使用镜头也非常有用,但这是另一篇即将发表的文章的主题。 随时鼓掌! 😎

更多有关Swift泛型的信息

这是什么? 关于仿制药的另一篇文章? 当然可以。 如果我们要确保只能为某些类型调用此函数,该怎么办? 例如,如果我们只想要Int ,而我们稍后将在代码中进行一些数学运算,该怎么办? 在这种情况下, Protocol即将解救。 现在我们重新营业! 我们现在有信心,您未来的自我不会改变任何事情,并确保您的安全和健康。 感谢过去的自我的关注。 因此,我希望您对Generics如何在Swift中工作有更好的了解。 说到扩展,我们实际上可以扩展Sequence并清理firstIn函数。 干净的代码是不错的代码。

iOS / Swift —创建多个动态选择器视图:快速教程

我一直在为我在Facebook上的项目完成最后的接触而努力,您可以在这里了解更多信息。 在我为应用程序püler贡献的所有功能中,我最喜欢的功能之一是汽车选择器视图: 为了实现这一点,了解UIPickerView对象和选择器视图与您拥有的数据进行交互的方式非常重要。 就我而言,我的数据结构如下所示: {make1:{model1:{year1,year2,…},model2:{year1,year2,…},…},make2:{…},…} 所以基本上是字典的字典,是的,我知道您现在可能已经讨厌我了,但请听我说。 由于汽车的工作结构,这是必要的,对于仍然不了解正在发生的事情的你们来说,看起来像这样。 (请原谅丑陋的图画^)。 无论如何,在宏伟的计划中,有多个品牌,每个品牌都有多个型号,每个型号都有几年。 现在,为了制作动态选择器视图,我们首先在情节提要中添加三个UIPickerViews并为其创建出口。 相应地命名,我的被称为: 之后,我们需要设置PickerViewDelegate,以便Xcode知道我们将为pickerView提供数据/指令,而不是它。 为此,请确保在类标题中添加UIPickerViewDataSource和UIPickerViewDelegate 。 在将这两个函数相加后,您应该得到错误消息,表明您尚未声明某些函数,但不要担心,我们马上就可以解决这些问题! 我们现在必须实现的三个功能是: func numberOfComponentsInPickerView(pickerView:UIPickerView)-> Int func pickerView(pickerView:UIPickerView,numberOfRowsInComponent组件:Int)-> Int func pickerView(pickerView:UIPickerView,titleForRow行:Int,forComponent组件:Int)->字符串? 第一个询问在pickerView中应包含多少个组件。 我选择有1,但是具有多个组件的pickerView看起来像这样: 我选择不使用它的原因是一个简单的设计决定,但是我的代码也可以进行编辑以适合这样的内容(也许会在将来发表文章?)。 因为我决定每个pickerView仅保留一个组件(并且在一个pickerView中不包含3个组件),所以我在函数中简单地返回了1: func numberOfComponentsInPickerView(pickerView:UIPickerView)-> Int { 返回1 } 现在来看两个稍微复杂的功能。 让我们开始 func pickerView(pickerView:UIPickerView,numberOfRowsInComponent组件:Int)-> Int 该函数要求每个组件中需要的行数,但是由于每个pickerViews只有一个组件,因此我们可以为每个组件返回一个特定的整数。 我们要返回makePicker的品牌数量, modelPicker的模型数量以及yearPicker的年数。 如果我们拥有所有品牌,型号和年份的阵列 ,那么我们要做的就是: func pickerView(pickerView:UIPickerView,numberOfRowsInComponent组件:Int)-> Int { 如果pickerView == makePicker { 返回listOfMakes.count } 否则,如果pickerView […]

Firebase电子邮件链接身份验证

Firebase提供了很棒的api来管理应用程序的后端。 今天,我们将在iOS App中实现Firebase电子邮件链接身份验证功能。 在我们开始之前,没有多少关于此功能的信息。 当用户点击该链接时,您可以发送电子邮件验证链接,它将用户重定向到应用,然后用户可以使用EMAIL和LINK登录。 注意:这是少密码登录。 您可以按照以下Firebase文档中的功能进行操作: 我将描述实现iOS应用程序此功能的所有必要步骤,请按照以下指南进行操作: 注意:为此功能,必须在 App Store Connect 上创建iOS应用 程序, 因为Firebase在创建动态链接时需要App Store ID和Team ID。 我不会介绍如何使用Firebase文档设置iOS App的出色表现。 完成设置后,安装Pod’Firebase / Auth’。 通过转到Firebase控制台“身份验证”选项卡,从顶部选择“登录方法”,以启用电子邮件链接(无密码)登录。 这是此功能的核心部分,请按照以下每个步骤仔细实施: 转到您的项目Firebase控制台,打开“动态链接”选项卡。 添加您的域,主要是Firebase建议的域名。 创建新的动态链接并设置短URL链接: 现在设置动态链接,复制您的授权域并粘贴在深层链接URL字段中。 授权域路径:Firebase控制台>身份验证>登录方法>向下滚动。 定义iOS链接行为,选择“在iOS应用中打开深层链接”,然后从下拉列表中选择应用捆绑包ID: 现在不需要4和5了,都按下一步创建它。 现在,我们必须在Xcode项目功能和信息选项卡中做两件事。 首先复制“动态链接域”,然后转到“ Xcode功能”选项卡,在“关联域”中创建一个并编写应用链接:并粘贴您的域。 其次,我们必须创建一个URL方案,打开Xcode Info选项卡并展开URL Types创建一个副本,然后将您的iOS App bundle标识符粘贴到URL Schemes字段中。 确保从下拉列表中选择“编辑者”角色,然后根据需要命名该标识符。 现在您已经准备好了,在继续进行编码之前,让我向您展示App UI,以便您了解功能和代码的工作方式。 我已经创建了用于输入电子邮件的简单文本字段和两个按钮,即“发送链接”按钮(用于向用户发送电子邮件)和“登录”按钮(用于在验证后使用电子邮件登录)。 您可以根据自己的需求自定义它。我已经创建了一个演示来实现此Firebase功能。 我希望你已经 在iOS应用和Pod’Firebase / Auth’中设置Firebase。 当用户单击“发送链接”按钮时,我们将通过调用firebase Auth.auth.SendSignInLink Api函数将身份验证链接发送到用户的电子邮件,该函数接受电子邮件和Parameters中的ActionCodeSetting。 ActionCodeSetting向Firebase提供有关如何构造电子邮件链接的说明。 下面是“发送链接”按钮操作的代码片段: […]

Swift World:设计模式-适配器

我们已经完成了创建模式,并将在本文中介绍结构模式。 从字面上看,结构模式与结构有关。 这意味着如何组织类或实例以形成更大的结构。 我们首先要讨论的是适配器模式。 现实世界中最新的适配器示例是闪电式3.5毫米耳机插孔适配器。 如果我们想在使用防雷连接器的新iPhone 7中重复使用旧耳机,则需要使用此适配器进行连接。 显然,耳机插孔适配器是适配器。 旧的3.5毫米耳机是适配器。 在编程世界中,适配器是我们要重用的老类。 但是其接口与新接口不兼容。 因此,我们需要一个适配器来帮助他们。 我们可以通过两种方式实现适配器模式。 第一个是使用合成的对象适配器。 适配器中有一个Adaptee实例来完成这项工作,如下图和代码告诉我们。 protocol Target { func request () } class Adapter : Target { var adaptee: Adaptee init (adaptee: Adaptee ) { self. adaptee = adaptee } func request () { adaptee .specificRequest () } } class Adaptee { func specificRequest () […]

iOS面试问题(快速)-第1部分

Swift的优点是什么? Swift是Apple Inc.为iOS ,macOS,watchOS,tvOS和Linux开发的通用,多范式, 编译时编程语言。 可读性 -简洁的语法,使阅读和编写变得更加容易。 维护 -更少的代码和更少的旧版-一个文件.swift。 更安全的平台 -您可以在编写代码时编译并修复错误。 高速 —令人难以置信的快速,高性能的LLVM编译器将Swift代码转换为优化的本机代码,以充分利用其设备。 还对语法和标准库进行了调整,以使代码发挥最佳性能。 Swift支持动态库 开源的 2.解释类和结构之间的差异。 快速的类和结构之间主要有四个区别。 类具有以下结构没有的其他功能: 类型转换 :类型转换使您可以在运行时检查和解释类实例的类型。 引用计数允许对一个类实例进行多个引用。 (结构是值类型,类是引用类型。) 继承使一个类可以继承另一个类的特征。 使用反初始化程序 ,类的实例可以释放其分配的所有资源。 结构在您的代码中传递时总是被复制,并且不使用引用计数。 结构实例总是按值传递,而类实例总是按引用传递。 3.何时使用类和何时使用struct? 作为一般准则,请考虑在以下一个或多个条件适用时创建结构: 该结构的主要目的是封装一些相对简单的数据值。 合理的是,当您分配或传递该结构的实例时,将封装的值复制而不是引用 。 结构存储的任何属性本身都是值类型,也应该期望将其复制而不是引用。 该结构不需要从另一个现有类型继承属性或行为 。 在所有其他情况下,定义一个类,并创建该类的实例以通过引用进行管理和传递。 4.如何传递变量作为参考? 我们可以使用inout参数将变量作为参考传递。 inout表示修改局部变量也将修改传入的参数。 var值:字串=“ Apple” func changeString(newValue:inout String){ newValue =“三星” print(newValue) //输出:三星 打印(值) //输出:三星 } changeString(newValue:&value) 5.什么是swift模块? 模块是代码分发的单个单元。 […]

Swift中的VIPER设计模式,用于iOS应用程序开发。

设计模式是上帝给软件开发人员的礼物。 这些技术可最大程度地减少代码重复,防止高度耦合并标准化编写代码的通用方式,从而为开发软件时的重复出现情况提供了通用解决方案。 在这个故事中,我们将熟悉用于iOS开发的称为VIPER (视图,交互器,演示者,实体和路由器)的设计模式。 先决条件:在开始学习VIPER之前,请确保您了解建筑设计模式和委托模式。 什么是毒蛇? 毒蛇是一种实现“关注分离”范式的设计模式。 与MVP或MVC一样,它通常采用模块化方法。 一种功能,一种模块。 对于每个模块,VIPER具有五个(有时是四个)不同的类,具有不同的角色。 任何课程都不能超出其唯一目的。 这些课程如下。 查看:具有所有代码的类,用于向用户显示应用程序界面并获取他们的响应。 在收到响应后,View会提醒演示者。 演示者:模块的核心。 它从视图中获取用户响应并相应地工作。 仅用于与所有其他组件进行通信的类。 调用路由器进行线框图,交互器以获取数据(网络调用或本地数据调用),查看以更新UI。 交互器:具有应用程序的业务逻辑。 主要进行API调用以从源中获取数据。 负责进行数据调用,但不一定来自其自身。 路由器:进行电线成帧。 从演示者那里收听要演示的屏幕并执行该屏幕。 实体:包含交互器使用的普通模型类。 下面显示了VIPER的简单示意图 毒蛇的例子 我创建了一个简单的项目来解释毒蛇。 可以在GitHub上找到。 这是一个非常基本的应用程序,它显示了从外部API获取的新闻标题。 (:p没用)。 Viper是委托驱动的体系结构。 因此,不同层之间的大多数通信都是通过委派执行的。 一层通过协议调用另一层。 调用层从协议中调用函数。 侦听层符合该协议并实现该功能。 下面,我将解释如何在我的一个示例项目中实现VIPER。 我建议您在github中打开项目并阅读说明。 通讯协定 我为所有协议创建了一个单独的文件。 遵循命名约定来命名协议。 例如,“ viewToPresenterProtocol”。 因此,这是一个“协议”,将由“演示者”实施以收听“视图”必须说的内容。 PresenterToViewProtocol:演示者调用,View侦听。 演示者从该协议接收引用以访问View。 视图符合协议。 ViewToPresenterProtocol:查看呼叫,主持人收听。 InteractorToPresenterProtocol:Interactor调用,Presenter监听。 PresentorToInterectorProtocol:演示者调用,Interactor侦听。 PresenterToRouterProtocol:演示者呼叫,路由器监听。 应用流程 View具有对“ ViewToPresenterProtocol”的引用以访问“ Presenter”并符合“ PresenterToViewProtocol”。 […]

将iOS和Mac异步和相关任务封装到Cocoa Operation子类中

异步任务(例如,从网络中获取数据,解析,处理数据并将数据保存到本地缓存中)是当今应用程序执行的常规任务。 作为开发人员,我们必须确保UI /主线程运行平稳,并将长时间运行的繁重工作任务移入后台线程,以维持60 FPS动画。 Apple为开发人员提供了两种在后台线程中执行任务的方式: 大中央调度(GCD):开发人员可以使用队列在后台线程池中串行或并发执行任务的API集。 操作(也称为NSOperation):可可抽象类,代表要执行的单个任务单元。 它是一个线程安全类,具有开箱即用的内置状态,优先级和QoS,取消和依赖项管理。 在本文中,我们将构建一个异步的Operation子类,该子类可从GitHub API异步获取存储库,以及一个从属的Operation子类,可将获取的存储库数据解析并将其序列化为Swift类。 我们将建立什么 AsynchronousOperation:支持异步操作的Operation子类。 FetchRepoOperation:AsynchronousOperation子类,该类从上周开始使用URLSession异步获取最新趋势的GitHub存储库的数据。 ParseRepoDataOperation:操作子类,该子类使用Swift Codable和JSONDecoder将FetchRepoOperation中的数据解码并序列化为GithubRepo对象的数组。 游乐场页面:使用OperationQueue执行操作,在操作之间添加依赖关系,并使用完成块在操作对象之间传递数据。 使用Operation子类实现异步操作 默认情况下,Operation Class同步运行代码。 Apple提供了一种通过子类化isAsynchronous布尔属性并将其重写为true来异步运行代码的方法。 我们还需要使用枚举添加我们自己的状态管理属性,处理从就绪,执行和完成状态的更改。 当同时读写状态属性时,dispacth队列将用于对状态属性使用调度屏障来处理同步。 在启动函数中,我们检查任务是否未取消,如果取消,则仅调用finish将状态更改为完成并返回。 如果没有,我们将状态设置为执行并调用主函数。 我们的子类将覆盖main函数,以执行函数内部的任务。 实现FetchRepoOperation来获取Github API FetchRepoOperation是异步操作的子类,我们声明两个可选属性,fetchedData是一个Data对象,将用于存储来自API调用的数据响应,以及一个error属性,将用于存储来自API的错误如果发生,请致电。 在超类的重写的main方法中,我们构造URL和查询项,这些查询项将查询自上周以来创建的存储库,该存储库按星数降序排列。 之后,我们初始化URLRequest并使用URLSession调用异步数据任务。 在数据任务完成处理程序内部,我们将响应数据和错误分配给实例属性,然后调用finish方法将操作的状态设置为finish,以将操作标记为完成。 实施ParseRepoOperation以使用Swift Codable类解码JSON数据 我们创建GithubRepoFetchResult,GithubRepo,GithubOwner Swift类,该类实现了codable和CodingKeys枚举,以将json属性名称映射到实例属性骆驼案例名称。 通过使用Codable,我们可以利用JSONDecoder将Data解码为自动实现Codable的类。 实现ParseRepoOperation非常简单,我们将Operation用作子类,因为JSONDecoder的解码功能是同步的,因此我们不需要使用AsynchronousOperation。 我们声明了3个可选的实例属性,fetchedData是从FetchRepoOperation传递的数据,如果将数据解码为对象时发生错误,则错误为Error对象,包含GitHubRepo的repos数组将用于将JSONDecoding的结果存储到对象中。 在main函数内部,我们使用guard来解包可选的fetchedData,如果为nil,我们只是从函数中返回。 之后,在try catch块中,我们使用JSONDecoder解码函数,将fetchedData和GithubRepoFetchResult作为要解码的根类。 然后,我们将GithubRepoFetchResult中的items属性分配给repos实例属性。 如果解码时发生错误,我们会将错误分配给我们的错误实例属性。 使用OperationQueue执行操作 为了执行操作,我们使用OperationQueue,它充当优先队列,该队列使用先进先出机制来处理操作的执行。 我们将maxConcurrentOperationCount设置为1,因此我们的操作不会同时执行。 我们实例化FetchRepoOperation和ParseRepoOperation对象,然后将FetchRepoOperation对象添加为ParseRepoOperation对象的依赖项,因此将首先启动fetch任务,并且必须完成分析任务才能开始。 在操作之间传递数据并不容易,有许多方法可以做到,例如使用包含数据的数据包装器引用类,然后将其传递给每个操作。 对于此实现,我们将使用操作完成块,该操作将在操作完成时调用。 我们参考解析和提取操作对象为提取操作完成块分配一个闭包。 使用Unowned来避免保留周期,在该块内部,我们将获取响应数据传递给了解析的fetchedData属性。 我们为解析操作完成块属性分配一个闭包,该闭包仅循环存储库并将打印库的名称打印到控制台,以便我们看到结果。 最后,要开始操作,我们将调用包含获取和解析操作的数组传递给OperationQueue的addOperations来开始任务。 结论 可可操作类为开发人员提供了极大的灵活性,例如任务之间的依赖性,调整队列优先级和QoS,执行后台任务时的取消和状态管理。 […]

Swift中的单向数据流架构(Redux)

我非常喜欢使用Swift的可重用和干净的架构。 在我的上一个项目中,我实现了MVVM和面向协议的体系结构,这有助于将关注点分开。 我也曾按照MVC方法从事过几个IOS项目。 在开始新项目或适应旧代码的同时选择正确的体系结构很重要。 我必须找到有关iOS体系结构模式的综述,如果您也感兴趣的话,可以介绍常见的体系结构。 iOS架构模式 揭秘MVC,MVP,MVVM和VIPER medium.com 但是有一种更好的方法(以我的拙见)主要是在我的经验中,MVVM在保持轻巧的同时做得很好,并且我确实在Swift中使用MVVM实现了单向数据流(Redux)方法,这产生了巨大的变化。 它使干净的体系结构稍微好一点,健壮且可测试 ,因此很重要 在本文中,我试图解释为什么将Redux与Swift结合使用是更好的主意。 首先,我们需要了解该概念的基础。 因此,让我们开始吧! 什么是Redux和单向数据流? Redux是一种流量实现,但实现略好一些,它使您的应用程序状态更加可预测。 它首先以强大的javascript库React作为JavaScript应用程序的可预测状态容器出现在Javascript世界中。 Redux,仅使应用程序的状态位于单个存储对象中。 因此,从应用程序/视图的生命周期状态触发操作会发生变化,而针对该操作的化简器会生成新状态。 关于Redux的另一种解释,您可以阅读Redux的卡通指南介绍 Redux的卡通简介 导致比Flux更加混乱的一件事是Flux和Redux之间的区别,这种模式以前是… code-cartoons.com 让我们开始; 在本文中,我使用了benjaminencz的ReSwift,后者在Swift上创建了Redux实现。 ReSwift是Swift中单向数据流架构的类似Redux的实现。 ReSwift可帮助您将应用程序组件的三个重要方面分开: 重新浏览参考 ReSwift正在快速发展,超越了核心库,它提供了用于路由和时间旅行的实验性扩展… reswift.github.io 状态:在ReSwift应用中,整个应用状态都明确存储在数据结构中。 这有助于避免复杂的状态管理代码,实现更好的调试,并具有许多其他好处…… 视图:在ReSwift应用程序中,当状态更改时,视图也会更新。 您的视图成为当前应用程序状态的简单可视化。 状态更改:在ReSwift应用中,您只能通过操作执行状态更改。 动作是描述状态变化的小数据。 通过极大地限制状态改变的方式,您的应用程序变得更易于理解,并且可以与许多协作者一起工作。 从ReSwift网站剪辑 用法 在这个简单的示例中,我声明了一个BaseState,因为struct带有两个状态。 BaseReducer已注册其减速器。 LoadingState struct处理显示正在加载微调器视图或隐藏视图的状态 ,是否具有isLoading属性知道当前正在加载? 它是在视图中旋转或在状态中旋转的类型。 然后,声明的LoadingAction具有两个结构, LoadingShowAction将类型传递为有效负载,而LoadingHideAction不包含任何有效负载。 最后,LoadingReducer采取操作( LoadingShowAction或LoadingHideAction )和当前状态,并生成一个新状态。 简单。 因此,此时我们可以调度一个动作,即; store.dispatch(LoadingShowAction(type:.Normal))显示微调器 store.dispatch(LoadingHideAction())隐藏微调框 。 […]