Swift UI初识及与Flutter的异同

Swift UI初识及与Flutter的异同

飞书文稿地址:https://xhoid9hw42.feishu.cn/docs/doccn6AIqg8WEphl1EHTWGrqstd

1、苹果官网对于SwiftUI的简单介绍

SwiftUI 于 2019 年度 WWDC 全球开发者大会上发布,它是基于 Swift 建立的声明式框架。 https://developer.apple.com/cn/xcode/swiftui/

1、支持全系列Apple设备,支持UI和动画的编写

以往Apple开发者需要对mac、ios、iwach等不同设备学习不同的编程Apple SDK,导致各个平台的都有一定的学习成本,而Swift UI做到了各个设备之间代码的一致性。并且Siwft编写的UI将由系统智能适配不同尺寸的屏幕,这也大大节约了屏幕适配的时间。

只要使用 SwiftUI,系统会默认支持白天和黑夜模式的自动切换;在各种尺寸的屏幕间自动适配;为任意控件添加 Haptic Touch 或是动画;在 Apple Watch 上带来独立而完整的体验;将iOS 的应用转换为 macOS 的原生应用,会以最快的速度支持第一方的各种新特性。这种对苹果硬件的深入支持是那些跨平台方案无论如何无法实现的。可以看一些采用第三方框架的知名应用,像是横屏、黑夜模式、小组件等基础的特性,到现在都迟迟没有适配。

2、声明式语法,维护更加简单,代码可读性更高

3、及时且灵活的实时预览工具

4、与UIKit彼此相容

一般开发者学习新技术有一个最大的障碍就是原先的项目怎么办。但 SwiftUI 在这一点上做的相当不错。由于是一个新发布的框架,UI 组件并不齐全,当 SwiftUI 中并没有提供类似的功能时,就可以把 UIKit 中已有的部分进行封装,提供给 SwiftUI 使用。需要做的仅仅是遵循UIViewRepresentable协议即可。相反,在已有的项目中,也可以仅用 SwiftUI 制作一部分的 UI 界面。 当然两种代码的风格是截然不同的,但在使用上却基本没有性能的损失。到最终成品时,用户也无法分辨出两种界面框架的不同。

2、开始构建一个简单的Swift UI项目

https://developer.apple.com/tutorials/swiftui/creating-and-combining-views 详细使用示例可通过以上链接下载官方Demo

1、Flutter简介

Flutter是谷歌的移动UI框架,采用谷歌Dart语言进行编程,可以快速在iOS和Android上构建“高质量”(打问号❓)的原生用户界面。 Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。 采用安卓内核渲染图形界面,因flutter效率上比react native要高,但是react native是通过桥接调用的原生的UI和交互,所以体验上最为接近原生。理论和实际体验flutter对于安卓支持是最好的,但是对于iOS因为用安卓内核重新渲染了push,pop等界面交互和图形操作,实际在处理复杂图形交互App的时候,体验依然不如iOS原生和react native。 https://flutterchina.club/

2、安装笔记

https://github.com/CoderHJZhao/flutter-dart

3、使用

4、Flutter和react native对比

https://juejin.cn/post/6937124684030967845

以目前i最新的OS和安卓手机图形渲染能力来看,一个第三方跨平台SDK的性能对比,只要不是太过份,其实对比都没有太大意义。毕竟用户能感知到的最明显的还是一个软件的交互和流畅度。以失去原生体验为前提而节省开发成本的跨平台开发,长远来看对用户来说都不是一件值得高兴的事。

而且长远来看,苹果每一次都对跨平台、跨审核的App和SDK重拳出击了,比如一开始的JS热更新、RA、到现在的flutter,事实证明,要想长远发展一个项目,用原生代码开发才是最具性价比的一件事。

🌈Cocoapods私有库创建方法总结

🌈Cocoapods私有库创建方法总结 创建Cocoapods私有库有一个全局了解,主要步骤总结如下:

  1. 创建私有Spec仓库
  2. 创建源码库,并git clone到本地
  3. 创建Cocoapods私有库模板
  4. 编写Cocoapods私有库podspec文件
  5. 添加源码和资源文件到指定的文件夹中(每次更改都需要在Example文件夹中pod install一下)
  6. 验证podspec文件是否无误
  7. 移动Cocoapods私有库模板中的文件到源码库
  8. 提交源码到git仓库,并打tag
  9. 将 podspec 提交到spec仓库
  10. 在项目中使用私有库

1、创建私有Spec仓库

执行repo 命令添加私有库Repo 打开终端,在任意目录下执行下面的命令:

pod repo add MOJiBaseSpec http://192.168.1.3/moji-cocoapods/MOJiBaseSpec.git

解释:
pod repo add 【私有库名称】【1.1中创建的远程仓库的git地址】

如果需要更改,用以下命令删除并重复上一步

pod repo remove REPO_NAME

成功后,我们将在以下目录中看到我们所创建的私有库文件夹

~/.cocoapods/repos

2、创建源码库,并git clone到本地

3、创建Cocoapods私有库模板

cd到你要储存项目的文件夹,使用以下命令创建Cocoapods私有库模板

pod lib create MOJiBase

紧接着,会有一些参数需要配置:(语言根据实际情况选择,生成的结构都是一样的,只是生成的默认语言会不一样)

配置完成后,会自动打开创建的项目

4、将pod创建的项目文件copy到MOJiBase本地仓库里

最终如如下图所示:

5、配置podspec文件

打开 MOJiBase本地仓库中的 Example 中的工程

选择 MOJiBasepodspec 文件进行编辑:

相关字段可以查询官方文档【Podspec Syntax Reference】。

以下是MOJiBase.podspec文件示例,仅供参考。

#
# Be sure to run `pod lib lint MOJiBase.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
#

Pod::Spec.new do |s|
 s.name           = 'MOJiBase'
 s.version        = '1.3.11'
 s.summary        = 'MOJi基础工具类库,其中包括一些UI功能,后期需要剥离'
 s.swift_version  = '5.0'

# This description is used to generate tags and improve search results.
#  * Think: What does it do? Why did you write it? What is the focus?
#  * Try to keep it short, snappy and to the point.
#  * Write the description between the DESC delimiters below.
#  * Finally, don't worry about the indent, CocoaPods strips it!

 s.description   = <<-DESC
TODO: Add long description of the pod here.
            DESC

 s.homepage      = 'http://192.168.1.3/moji-cocoapods/mojibase.git'
 # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
 s.license       = { :type => 'MIT', :file => 'LICENSE' }
 s.author        = { '赵汉军' => 'zhaohanjun@mail.shareintelli.com' }
 s.source        = { :git => 'http://192.168.1.3/moji-cocoapods/mojibase.git', :tag => s.version.to_s }
 # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'

 s.ios.deployment_target = '11.0'

 s.source_files = 'MOJiBase/Classes/**/*'
  
 s.resource_bundles = {
  #'MOJiBase' => ['MOJiBase/Assets/*.png']
  #'MOJiBase' => ['MOJiBase/base-images/*.png']
  'MOJiBase'  => ['MOJiBase/**/*.{xib,bundle}']
#  'MOJiBase' => ['MOJiBase/Assets/base-images.bundle']
 }

 # s.public_header_files = 'Pod/Classes/**/*.h'
  s.frameworks = 'UIKit', 'Foundation'
  s.dependency 'SVProgressHUD'
  s.dependency 'SwiftyJSON'
  s.dependency 'SnapKit'
  
end

相关字段解释:

//私有库对应的版本号
s.version     = '1.3.11'
//私有库描述,最好写一下,不然最终验证的时候会报警告⚠️
s.summary     = 'MOJi基础工具类库,其中包括一些UI功能,后期需要剥离'
//如果使用的是swift,需要指定使用的版本,否则有可能编译不过,最终验证也会报警告
s.swift_version  = '5.0'
//仓库地址,必须得写,否则后面spec文件会上传不成功
s.source      = { :git => 'http://192.168.1.3/moji-cocoapods/mojibase.git', :tag => s.version.to_s }
//源码文件路径
s.source_files = 'MOJiBase/Classes/**/*'
//指定的资源文件读取路径,xib、png、bundle、Assets都需要指定
s.resource_bundles = {
 'MOJiBase' => ['MOJiBase/**/*.{xib,bundle}']
}
//依赖的系统库
s.frameworks = 'UIKit', 'Foundation'
//依赖的第三方库
s.dependency 'SnapKit'

提示:每次更改podspec文件都必须pod install才会生效

6、添加源码和资源文件:

如图所示:

Classes存放所有的代码文件Assets存放所有的资源文件

1、访问图片资源、bundle、xib等文件

1、因为pod会将我们的bundle、xib、png等资源文件打包进pod项目的mainBundle中的Base.framework中,所以不能用以前的方式直接读取图片,而且实用NSBundle.mainBundle也容易和主项目产生冲突。

OC版本:新建NSBundle+Extension分类

+ (NSBundle *)bundleWithBundleName:(NSString *)bundleName podName:(NSString *)podName {
  if (bundleName == nil && podName == nil) {
    @throw @"bundleName和podName不能同时为空";
  }else if (bundleName == nil ) {
    bundleName = podName;
  }else if (podName == nil) {
    podName = bundleName;
  }
   
   
  if ([bundleName containsString:@".bundle"]) {
    bundleName = [bundleName componentsSeparatedByString:@".bundle"].firstObject;
  }
  //没使用framwork的情况下
  NSURL *associateBundleURL = [[NSBundle mainBundle] URLForResource:bundleName withExtension:@"bundle"];
  //使用framework形式
  if (!associateBundleURL) {
    associateBundleURL = [[NSBundle mainBundle] URLForResource:@"Frameworks" withExtension:nil];
    associateBundleURL = [associateBundleURL URLByAppendingPathComponent:podName];
    associateBundleURL = [associateBundleURL URLByAppendingPathExtension:@"framework"];
    NSBundle *associateBunle = [NSBundle bundleWithURL:associateBundleURL];
    associateBundleURL = [associateBunle URLForResource:bundleName withExtension:@"bundle"];
  }
   
  NSAssert(associateBundleURL, @"取不到关联bundle");
  //生产环境直接返回空
  return associateBundleURL?[NSBundle bundleWithURL:associateBundleURL]:nil;
}

Swift版本

extension Bundle {
   
  class func fetchBundle(bundleName: String?, podName: String?) -> Bundle? {
    var bundleName = bundleName
    var podName  = podName
    //判断参数
    if bundleName == nil && podName == nil {
      assert((bundleName != nil), "bundleNameh 和 podName不能同时为空")
    } else if (bundleName == nil) {
      bundleName = podName
    } else if (podName == nil) {
      podName = bundleName
    }
    //适配名称
    if ((bundleName?.contains(".bundle")) != nil) {
      bundleName = bundleName?.components(separatedBy: ".bundle").first
    }
    var associateBundleURL = Bundle.main.url(forResource: bundleName, withExtension: "bundle")
    //如果associateBundleURL为空,则走下面方法
    if associateBundleURL == nil {
      associateBundleURL = Bundle.main.url(forResource: "Frameworks", withExtension: nil)
      associateBundleURL = associateBundleURL?.appendingPathComponent(podName!)
      associateBundleURL = associateBundleURL!.appendingPathExtension("framework")
      let associateBunle = Bundle(url: associateBundleURL!)
      associateBundleURL = associateBunle?.url(forResource: bundleName, withExtension: "bundle")
    }
    assert((associateBundleURL != nil), "取不到关联bundle")

    return Bundle(url: associateBundleURL!) ?? nil
     
  }
}

7、验证podspec文件是否无误

1、主要使用命令

cd 到 MOJiBase项目文件夹下。其目录下有 MOJiBase.podspec 文件。

执行以下命令:

pod lib lint --allow-warnings
#严谨一点,可以不忽略警告⚠️,如下
pod lib lint

如果验证成功将会显示如下图:

如果报Error可以用如下命令查看详细错误信息:

pod lib lint --verbose 

2、常见的几个警告⚠️信息

1、summary: The summary is not meaningful.

解释:这是因为你没有修改 .podspec 文件中的 s.summary 字段。只需要修改 .podspec 文件即可,如下:

s.summary          = '这是一个测试组件库'

2、swift: The validator used Swift 3.2 by default because no Swift version was specified

解释:没有指定Swift版本,只需要在 .podspec 增加对swift版本指定即可,如下:

s.swift_version  = '5.0'

解决完上面两个WARN之后,再执行pod lib lint --allow-warnings 命令就会发现没有警告了。

后续欢迎补充更多“常见错误”以帮助开发小伙伴避坑!

8、提交MOJiBase到gitlab仓库

按照如下步骤进行git操作:

git add --all
git commit -a -m "第一次提交"
git push origin master
git tag 1.3.11
git push --tags

9、将podspec提交到gitlab

cd 到 MOJiBase项目文件夹下。其目录下有MOJiBase.podspec 文件。运行如下命令:

pod repo push BOTestSpec MOJiBase.podspec --allow-warnings
#命令解释
#pod repo push 【私有库名称】 【podspec文件名】 --allow-warnings

上传成功后将会显示如下信息:

显示如上信息后,你还可以查看 ~/.cocoapods/repos。在 MOJiBaseSpec 仓库下会多新增 MOJiBase 文件夹。如下所示:

至此,你的私有库已制作完成。你和你的小伙伴们可以在项目中使用它了。

10、在你的项目中使用你的私有库

1、创建MOJiBaseTest测试项目。

2、在项目目录下,执行命令:

pod init

3、编辑 podfile 文件:

platform :ios, '9.0' 
source 'http://192.168.1.3/moji-cocoapods/mojibase.git' # 官方库
source 'http://192.168.1.3/moji-cocoapods/MOJiBaseSpec.git'# 私有库Repo地址 
target 'BOSpecDemo' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 
use_frameworks! 
# Pods for MOJiBaseTest 
pod 'MOJiBase' 
end

注意:私有仓库的地址一定是Spec Repo 的地址,不要错误的使用MOJiBase组件的git仓库。

然后执行 pod install,添加私有库到工程中。