目录

  • 新建项目
  • 设置相关参数
  • 1.设置静态库
  • 2.设置版本号
  • 3. 设置生成fat包
  • 4. Dead Code Stripping(NO)
  • 创建swift系统类扩展
  • 添加脚本
  • 脚本使用教程
  • 新建脚本项目
  • 在脚本项目中添加脚本
  • 合并framework静态库的脚本内容
  • 运行脚本
  • swift代码调用我们的swift静态库
  • OC使用swift编写的framework



之前我写了一篇名为:xcode写framework静态库脚本文件合并fat文件教程和踩坑的文章:


但还是没说具体怎么用swift建立framework静态库,这里补上教程:

新建项目

File->new->Project->Framework,起一个项目名,语言选择swift 如下图

swift静态派发 swift 静态库_framework


swift静态派发 swift 静态库_swift_02

设置相关参数

1.设置静态库

build settings -> Mach-O Type设置(Static Library) ,如下图

swift静态派发 swift 静态库_swift静态派发_03

2.设置版本号

general->Deployment Info -> 设置 ios9.0,

swift静态派发 swift 静态库_静态库_04

3. 设置生成fat包

Build Active Architecture Only(NO,生成fat包,包括模拟器x86_64和arm64)

swift静态派发 swift 静态库_framework_05

4. Dead Code Stripping(NO)

swift静态派发 swift 静态库_swift静态派发_06

创建swift系统类扩展

新建文件一个swift文件,起名UIView+AnimateExtension.swift,这个系统类扩展作用就是执行以后可以直接摇动UIView

//
//  UIView+AnimateExtension.swift
//  tdwUIView+AnimateExtension
//
//  Created by tdw on 2021/1/5.
//

import UIKit

extension UIView{
    /**
     摇动当前view水平方向
     */
    @objc public func shakeAnimationH() {
        // 抖一抖
        self.layer.removeAnimation(forKey: "error")
        let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
        animation.values = [-16, 0, 16, 0]
        animation.duration = 0.2
        animation.repeatCount = 3
        self.layer.add(animation, forKey: "error")
    }
    /**
     摇动当前view水平方向
     */
    @objc public func shakeAnimationH(duration:Double,repeatCount:Float) {
        // 抖一抖
        self.layer.removeAnimation(forKey: "error")
        let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
        animation.values = [-16, 0, 16, 0]
        animation.duration = duration
        animation.repeatCount = repeatCount
        self.layer.add(animation, forKey: "error")
    }
    /**
     摇动当前view垂直方向
     */
    @objc public func shakeAnimationV() {
        // 抖一抖
        self.layer.removeAnimation(forKey: "error")
        let animation = CAKeyframeAnimation(keyPath: "transform.translation.y")
        animation.values = [0, -8, 0, 8]
        animation.duration = 0.2
        animation.repeatCount = 3
        self.layer.add(animation, forKey: "error")
    }
    
    @objc public func shakeAnimationV(duration:Double,repeatCount:Float) {
        // 抖一抖
        self.layer.removeAnimation(forKey: "error")
        let animation = CAKeyframeAnimation(keyPath: "transform.translation.y")
        animation.values = [0, -8, 0, 8]
        animation.duration = duration
        animation.repeatCount = repeatCount
        self.layer.add(animation, forKey: "error")
    }
    /**
     改变透明度文字的动画
     
     @param withDuration 持续5秒,然后减淡
     */
    @objc public func alphaAnimate(_ withDuration:Double = 5){
        self.alpha = 0
        UIView.animate(withDuration: 1, animations: {
            self.alpha = 1
        }) { (_) in
            UIView.animate(withDuration: withDuration) {
                self.alpha = 0
            }
        }
    }
}

还可以添加其他的类扩展,或者方法,如下图,我这个之前封装的里面放了2个swift文件,UIView+addGes.swift这个是添加手势的类扩展,这里不做演示,

swift静态派发 swift 静态库_swift静态派发_07


这里为了教程清晰,我只添加一个swift文件

swift静态派发 swift 静态库_静态库_08


点击左上角模拟器和手机符号左边的按钮,选择 edit Scheme ->run -> Build configuration -> Release,如下图

swift静态派发 swift 静态库_静态库_09

swift静态派发 swift 静态库_swift_10

swift静态派发 swift 静态库_静态库_11


这时候我们,默认情况下,我们选择模拟器运行,command + b ,生成 x86_64 arm64 和 i386 然后在Products里面show in finder查看framework里面的静态库,再选一个真机或者ios设备,command + b生成 arm64和armv7 ,如下图,分别打开真机目录和模拟器目录

swift静态派发 swift 静态库_swift_12


swift静态派发 swift 静态库_swift静态派发_13


如果这时候我们用lipo -create 文件名1 文件名2 -output 文件名3来合并文件,就会报错::tdwUIView_Extension have the same architectures (arm64) and can’t be in the same fat output file

原因是2个文件里面都有arm64,重复了。

解决方法1(不推荐,因为要手动输入命令,而且每次运行都要手动删除一次arm64)

lipo tdwUIView_Extension -remove arm64 -output tdwUIView_Extension_1

然后再把生成的tdwUIView_Extension_1 和 另一个Release-iphoneos 或者 Release-iphonesimulator 文件夹下的tdwUIView_Extension合并起来 用 lipo -create 文件路径1 文件路径2 -output 合并后的文件名

解决方法2(推荐,一次设置永久方便)

build Settings里面搜索:excluded architectures

然后如图点击release里面 选择 Any ios simulator sdk,输入arm64

这部的操作是把模拟器生成的framework都排除arm64格式,以后模拟器生成的只包含x86_64 i386 2种格式。如下图操作

swift静态派发 swift 静态库_framework_14

添加脚本

脚本使用教程

新建脚本项目

首先点击xcode左上角的项目,然后在中间targets下面点加号+,如图

swift静态派发 swift 静态库_ios_15


在弹出框里面,选择other->Aggregate->next,如下图:

swift静态派发 swift 静态库_swift静态派发_16


项目名随便起一个,我起名叫:shell

project默认就是当前项目

swift静态派发 swift 静态库_swift静态派发_17

在脚本项目中添加脚本

target选中新建的项目shell->Build Phases,点击上面的+加号,选中New Run Script Phase

swift静态派发 swift 静态库_swift静态派发_18


点击Run Script,然后在下面中间填写脚本,如下图:

swift静态派发 swift 静态库_静态库_19

合并framework静态库的脚本内容

下面是脚本内容,复制到上图的红框里面

#设置输出文件夹
output_path=${PROJECT_DIR}
#设置项目文件名
xcodeproj_name=${PROJECT_NAME}.xcodeproj

# 创建输出路径,-p递归创建目录,会把子目录也创建出来
mkdir -p "${output_path}"
 


#编译真机版本项目
xcodebuild -project "${xcodeproj_name}" -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphoneos ONLY_ACTIVE_ARCH=NO   BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
 #编译模拟器版本项目
xcodebuild -project "${xcodeproj_name}" -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
 
#复制真机目录的文件到项目目录
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${output_path}/"
 
  
 

#给模拟器的Modules文件夹下的.swiftmodule文件夹赋值
simulator_modules_path="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/."
#如果.swiftmodule文件夹存在,就复制到项目目录
if [ -d "${simulator_modules_path}" ]; then
 
cp -R "${simulator_modules_path}" "${output_path}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
 
fi
 
  
 
#合并framework文件
 
lipo -create -output "${output_path}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"
 
  

#删除build文件夹
rm -rf "${PROJECT_DIR}/build"
#拼接项目名.framework/Headers/项目名-Swift.h 文件名
fileSwift="${PROJECT_DIR}/${PROJECT_NAME}.framework/Headers/${PROJECT_NAME}-Swift.h"

echo "file-Swift.h==============$fileSwift"


#在 项目名.framework/Headers/项目名-Swift.h里面修改内容内容

str1="#if 0"
str2="#elif defined(__arm64__) && __arm64__"
str3="#if defined(__x86_64__) && __x86_64__ || (__arm64__) && __arm64__"
#修改
#查找#if 0替换成空
sed -i '' 's/#if 0//g' $fileSwift
#查找#elif defined(__arm64__) && __arm64__替换成空
sed -i '' 's/#elif defined(__arm64__) && __arm64__//g' $fileSwift
#在1第一行添加字符串#if defined( __x86_64__ ) && __x86_64__ || (__arm64__) && __arm64__
sed -i '' "1 a\\
$str3" $fileSwift
# 打开项目目录,项目名.framework文件就是我们要用的静态库
open "${PROJECT_DIR}"

运行脚本

然后选择shell项目,并且选择build 的设备,如下图,我build成真机设备

swift静态派发 swift 静态库_framework_20


然后按command + B编译

就会自动执行这个脚本,执行结果会合并framework的Modules文件夹,并且会把-Swift.h自动替换成 真机和模拟器都能使用的文件,shell每一句我都加了注释

swift代码调用我们的swift静态库

这里新建项目啥的我就不用演示了,直接就把注意点告诉大家
拖拽刚才的framework文件夹,注意add to targets必须选,选到ios app的target里,如果不选,运行以后,再调用方法的时候会找不到方法报错如下:
reason: ‘-[UIView shakeAnimationH]: unrecognized selector sent to instance 0x105d05c60’

swift静态派发 swift 静态库_framework_21


然后单独拖拽headers文件夹,这样就可以使用里面的头文件了

swift静态派发 swift 静态库_静态库_22

因为添加了系统类的类扩展,所以调用的时候需要在调用项目中添加:

build setting->other linker Flags -ObjC

如果不添加会找不到方法,因为只有头文件能编译,但是链接的时候没有找到函数定义,报错:reason: ‘-[UITextField shakeAnimationH]: unrecognized selector sent to instance 0x100f0d2f0’

swift静态派发 swift 静态库_swift静态派发_23


在要使用的swift文件,不需要包含.h文件,但是要import模块:import + framework名

import tdwUIView

这样就能使用了,如下面代码:

//
//  ViewController.swift
//  RunLibDemo
//
//  Created by tdw on 2021/1/5.
//

import UIKit
import tdwUIView 
class ViewController: UIViewController {
    
    @IBOutlet weak var view1: UIView!
    @IBOutlet weak var tf1: UITextField!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        view1.tdwView.addGesture(.Tap) {
            self.view1Fn()
        }
    
        
    }
    func view1Fn(){
        print("点击")
    }
    @IBAction func btn1Click(_ sender: Any) {
        if tf1.text == ""{
            print("摇动")
            tf1.shakeAnimationH()//这个就是我们封装的framework类扩展
        }
    }
    
    @IBAction func ocVCJumpBtnClick(_ sender: Any) {
        let vc = OCVC1()
        navigationController?.pushViewController(vc, animated: true)
    }
}

OC使用swift编写的framework

在运行的ios app的tagrget里面选择build setting -> always embed swift standard libraries - > YES

swift静态派发 swift 静态库_framework_24

在要oc文件中包含:#import “framework名字-Swift.h”,因为这个文件是xcode自动生成的,把这个文件打开移动到最下面,会看到自动导出的方法名

swift静态派发 swift 静态库_ios_25

//
//  OCVC1.m
//  RunLibDemo
//
//  Created by tdw on 2021/1/6.
//

#import "OCVC1.h"
#import "tdwUIView-Swift.h"//使用静态库之前,先把framework文件夹下的headers文件夹拖拽到项目目录,然后再#import项目名-Swift.h
@interface OCVC1 ()
@property (weak, nonatomic) IBOutlet UITextField *tf1;
@property (weak, nonatomic) IBOutlet UIView *view1;

@end

@implementation OCVC1

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    
    [self.view1 shakeAnimationH];
    
}
- (IBAction)btnClick:(id)sender {
    if ([self.tf1.text isEqualToString:@""]){
        [self.tf1 shakeAnimationH];//调用frame静态库方法
    }
}


@end