├── .gitignore ├── AxcUIKit.podspec ├── AxcUIKit ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── AppKit │ ├── Basic │ │ └── asd.swift │ ├── ConsoleView │ │ ├── AxcConsoleView+Api.swift │ │ └── AxcConsoleView.swift │ └── TargetDragView │ │ ├── AxcTargetDragView+Api.swift │ │ └── AxcTargetDragView.swift │ ├── Core │ ├── AxcUIKitLib.swift │ └── Protocol │ │ ├── AxcUIBasicFuncTarget.swift │ │ └── AxcUICallbackTarget.swift │ ├── CrossPlatform │ ├── Basic │ │ ├── View │ │ │ ├── AxcView+Api.swift │ │ │ └── AxcView.swift │ │ └── ViewController │ │ │ ├── AxcViewController+Api.swift │ │ │ └── AxcViewController.swift │ ├── BubbleView │ │ ├── AxcBubbleView+Api.swift │ │ └── AxcBubbleView.swift │ ├── GradientView │ │ ├── AxcGradientView+Api.swift │ │ └── AxcGradientView.swift │ ├── Label │ │ ├── AxcLabel+Api.swift │ │ ├── AxcLabel.swift │ │ └── Bridge │ │ │ ├── _AxcNSLabel.swift │ │ │ └── _AxcUILabel.swift │ ├── TableView │ │ ├── AxcTableView+Api.swift │ │ ├── AxcTableView.swift │ │ └── Bridge │ │ │ ├── _AxcNSTableView.swift │ │ │ └── _AxcUITableView.swift │ └── TableViewCell │ │ └── AxcTableViewCell.swift │ ├── QuartzCore │ ├── GradientLayer │ │ ├── AxcGradientLayer+Api.swift │ │ └── AxcGradientLayer.swift │ └── Layer │ │ ├── AxcLayer+Api.swift │ │ └── AxcLayer.swift │ └── UIKit │ └── Views │ └── Basic │ └── AxcCollectionView.swift ├── Example ├── AxcUIKit.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ └── macOS.xcscheme ├── Podfile ├── iOS │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift ├── iOSTests │ └── iOSTests.swift ├── iOS_Widget │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── WidgetBackground.colorset │ │ │ └── Contents.json │ ├── Info.plist │ ├── iOS_Widget.intentdefinition │ ├── iOS_Widget.swift │ ├── iOS_WidgetBundle.swift │ └── iOS_WidgetLiveActivity.swift ├── macOS │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ └── Main.storyboard │ ├── ViewController.swift │ └── macOS.entitlements └── macOSTests │ └── macOSTests.swift ├── LICENSE ├── README.md └── _Pods.xcodeproj /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | *.xcworkspace 20 | !default.xcworkspace 21 | *.xcuserstate 22 | 23 | 24 | 25 | #CocoaPods 26 | Pods 27 | *.lock 28 | Podfile.lock 29 | -------------------------------------------------------------------------------- /AxcUIKit.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `s.dependency spec lint AxcBadrock.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | s.name = "AxcUIKit" 12 | 13 | # UI控件库版本 14 | s.version = "1.0.0" 15 | 16 | s.swift_version = '5.0' 17 | 18 | s.summary = "AxcUI控件库" 19 | 20 | s.homepage = "https://github.com/axclogo/AxcUIKit-Swift" 21 | 22 | s.license = { :type => "MIT", :file => "LICENSE" } 23 | 24 | s.author = { "赵新" => "axclogo@163.com" } 25 | 26 | s.social_media_url = "https://github.com/axclogo/" 27 | 28 | s.source = { :git => "https://github.com/axclogo/AxcUIKit-Swift.git", :tag => s.version } 29 | 30 | s.static_framework = true 31 | 32 | s.requires_arc = true 33 | 34 | s.pod_target_xcconfig = { 35 | 'CODE_SIGNING_ALLOWED' => 'NO' 36 | } 37 | 38 | # 设置子库 ===> 39 | # 文件类型 40 | fileType = "{swift,h,m,mm,cpp}" 41 | # 基础路径 42 | baseFilePath = "AxcUIKit/Classes" 43 | 44 | # 核心文件(不可或缺) 45 | Core_files = [ 46 | "#{baseFilePath}/Core/**/*.#{fileType}" 47 | ] 48 | # 跨平台文件 49 | CrossPlatform_files = "#{baseFilePath}/CrossPlatform/**/*.#{fileType}" 50 | QuartzCore_files = "#{baseFilePath}/QuartzCore/**/*.#{fileType}" 51 | 52 | 53 | #iOS平台 54 | s.ios.deployment_target = '11.0' 55 | 56 | # iOS平台文件 57 | ios_source_files = 58 | Core_files + 59 | [ 60 | "#{baseFilePath}/UIKit/**/*.#{fileType}", # 主要文件 61 | CrossPlatform_files, 62 | QuartzCore_files 63 | ] 64 | s.ios.source_files = ios_source_files 65 | 66 | 67 | # macOS平台 68 | s.osx.deployment_target = '11.0' 69 | 70 | # macOS平台文件 71 | osx_source_files = 72 | Core_files + 73 | [ 74 | "#{baseFilePath}/AppKit/**/*.#{fileType}", # 主要文件 75 | CrossPlatform_files, 76 | QuartzCore_files 77 | ] 78 | s.osx.source_files = osx_source_files 79 | 80 | 81 | # 核心 82 | s.subspec 'Core' do |c| 83 | c.source_files = Core_files 84 | c.dependency 'AxcBedrock' 85 | c.dependency 'SnapKit' 86 | end 87 | 88 | 89 | # # iOS平台 90 | # s.subspec 'iOS' do |c| 91 | # # Objs对象类 92 | # s.subspec 'Objs' do |c| 93 | # # Transition转场动画 94 | # c.subspec 'Transition' do |c| 95 | # c.source_files = "AxcUIKit/Classes/Objs/Transition/**/*.#{fileType}" 96 | # c.dependency 'AxcUIKit/Core' 97 | # end 98 | # 99 | # # Present模态器 100 | # c.subspec 'Present' do |c| 101 | # c.source_files = "AxcUIKit/Classes/Objs/Present/**/*.#{fileType}" 102 | # c.dependency 'AxcUIKit/Objs/Transition' 103 | # end 104 | # 105 | # # Collection布局器 106 | # c.subspec 'CollectionLayout' do |c| 107 | # c.source_files = "AxcUIKit/Classes/Objs/CollectionLayout/**/*.#{fileType}" 108 | # c.dependency 'AxcUIKit/Core' 109 | # end 110 | # 111 | # end 112 | # 113 | # # 视图控件类 114 | # s.subspec 'Views' do |c| 115 | # # 基础视图 116 | # c.subspec 'View' do |c| 117 | # c.source_files = "AxcUIKit/Classes/Views/View/**/*.#{fileType}" 118 | # c.dependency 'AxcUIKit/Core' 119 | # end 120 | # 121 | # # 气泡视图 122 | # c.subspec 'BubbleView' do |c| 123 | # c.source_files = "AxcUIKit/Classes/Views/BubbleView/**/*.#{fileType}" 124 | # c.dependency 'AxcUIKit/Views/View' 125 | # c.dependency 'SnapKit' 126 | # end 127 | # 128 | # end 129 | # end 130 | # 131 | # # MacOS平台 132 | # s.subspec 'MacOS' do |c| 133 | # end 134 | # 135 | # 136 | # # 通用平台 137 | # # 图片对象,支持网络本地加载 138 | # s.subspec 'Image' do |c| 139 | # c.source_files = "AxcUIKit/Classes/Objs/Image/**/*.#{fileType}" 140 | # c.dependency 'AxcUIKit/Core' 141 | # c.dependency 'Kingfisher' 142 | # end 143 | # 144 | # # 文字对象,支持富文本、普通文字等相互转换 145 | # s.subspec 'Text' do |c| 146 | # c.source_files = "AxcUIKit/Classes/Objs/Text/**/*.#{fileType}" 147 | # c.dependency 'AxcUIKit/Core' 148 | # end 149 | # 150 | # # 大小对象,支持比率、数值大小 151 | # s.subspec 'Size' do |c| 152 | # c.source_files = "AxcUIKit/Classes/Objs/Size/**/*.#{fileType}" 153 | # c.dependency 'AxcUIKit/Core' 154 | # end 155 | end 156 | -------------------------------------------------------------------------------- /AxcUIKit/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/axclogo/AxcUIKit-Swift/069ac702c41eccb1453c6f080eaae4a1dafea7f7/AxcUIKit/Assets/.gitkeep -------------------------------------------------------------------------------- /AxcUIKit/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/axclogo/AxcUIKit-Swift/069ac702c41eccb1453c6f080eaae4a1dafea7f7/AxcUIKit/Classes/.gitkeep -------------------------------------------------------------------------------- /AxcUIKit/Classes/AppKit/Basic/asd.swift: -------------------------------------------------------------------------------- 1 | // 2 | // asd.swift 3 | // AxcUIKit 4 | // 5 | // Created by 赵新 on 26/9/2023. 6 | // 7 | 8 | import Cocoa 9 | 10 | class AxcSplitViewController: NSSplitViewController { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/AppKit/ConsoleView/AxcConsoleView+Api.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcConsoleView+Api.swift 3 | // Alamofire 4 | // 5 | // Created by 赵新 on 26/9/2023. 6 | // 7 | 8 | import AxcBedrock 9 | 10 | // MARK: - [AxcConsoleViewApi] 11 | 12 | public protocol AxcConsoleViewApi { } 13 | 14 | extension AxcConsoleViewApi where Self: AxcConsoleView { } 15 | 16 | // MARK: - AxcTargetDragView + AxcUICallbackTarget 17 | 18 | public extension AxcUICallback where Base: AxcConsoleView { 19 | } 20 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/AppKit/ConsoleView/AxcConsoleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcConsoleView.swift 3 | // Alamofire 4 | // 5 | // Created by 赵新 on 26/9/2023. 6 | // 7 | 8 | import AxcBedrock 9 | 10 | public extension AxcConsoleView { 11 | func _append(log: String) { 12 | let operation = BlockOperation { [weak self] in // 创建一个Operation任务 13 | guard let weakSelf = self else { return } 14 | let textView = weakSelf._textView 15 | // 是否滑动 16 | let isSmartScroll = textView.visibleRect.maxY == textView.bounds.maxY 17 | // 拼接 18 | let attributedString = NSAttributedString(string: "\(log)\n", 19 | attributes: weakSelf._textAttributed) 20 | textView.textStorage?.append(attributedString) 21 | // 如果在最底部则继续滚动 22 | if isSmartScroll { 23 | textView.scrollToEndOfDocument(self) 24 | } 25 | // 更新日志进度 26 | weakSelf.updateLogProgressIndicator() 27 | } 28 | _logOperationQueue.addOperation(operation) 29 | _logOperationQueue.progress.totalUnitCount += 1 30 | } 31 | 32 | func updateLogProgressIndicator() { 33 | let totalUnitCount = Double(_logOperationQueue.progress.totalUnitCount) 34 | guard totalUnitCount > 0 else { return } 35 | let completedUnitCount = Double(_logOperationQueue.progress.completedUnitCount) 36 | _logProgressIndicator.maxValue = totalUnitCount 37 | _logProgressIndicator.doubleValue = completedUnitCount 38 | } 39 | } 40 | 41 | // MARK: - [AxcConsoleView] 42 | 43 | open class AxcConsoleView: AxcView { 44 | open override func makeUI() { 45 | super.makeUI() 46 | set(backgroundColor: NSColor.black) 47 | 48 | addSubview(_logProgressIndicator) 49 | _logProgressIndicator.snp.makeConstraints { make in 50 | make.top.left.right.equalToSuperview() 51 | } 52 | 53 | addSubview(_scrollView) 54 | _scrollView.snp.makeConstraints { make in 55 | make.top.equalTo(_logProgressIndicator.snp.bottom) 56 | make.left.right.bottom.equalToSuperview() 57 | } 58 | } 59 | 60 | 61 | private lazy var _logOperationQueue: OperationQueue = { 62 | let queue = OperationQueue.main 63 | queue.maxConcurrentOperationCount = 1 64 | return queue 65 | }() 66 | 67 | private lazy var _scrollView: NSScrollView = { 68 | let scrollView = NSScrollView() 69 | scrollView.backgroundColor = NSColor.black 70 | scrollView.hasVerticalScroller = true 71 | scrollView.documentView = _textView 72 | return scrollView 73 | }() 74 | 75 | private lazy var _textAttributed: [NSAttributedString.Key: Any] = { 76 | let fontSize: CGFloat = 6 77 | var attributedFont = fontSize.axc.nsFont 78 | if let font = NSFont(name: "Monaco", size: fontSize) { 79 | attributedFont = font 80 | } 81 | let textColorAttribute: [NSAttributedString.Key: Any] = [ 82 | .foregroundColor: NSColor.white, 83 | .font: attributedFont, 84 | ] 85 | return textColorAttribute 86 | }() 87 | 88 | private lazy var _logProgressIndicator: NSProgressIndicator = { 89 | let progressIndicator = NSProgressIndicator() 90 | progressIndicator.isIndeterminate = false 91 | progressIndicator.isBezeled = true 92 | progressIndicator.style = .bar 93 | progressIndicator.minValue = 0 94 | return progressIndicator 95 | }() 96 | 97 | private lazy var _textView: NSTextView = { 98 | let textView = NSTextView() 99 | textView.backgroundColor = NSColor.clear 100 | textView.isEditable = false 101 | textView.drawsBackground = false 102 | textView.toggleAutomaticQuoteSubstitution(nil) 103 | textView.toggleGrammarChecking(nil) 104 | textView.toggleAutomaticDashSubstitution(nil) 105 | textView.isHorizontallyResizable = false 106 | textView.isVerticallyResizable = true 107 | textView.autoresizingMask = .width 108 | textView.textContainer?.containerSize = NSSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) 109 | textView.textContainer?.widthTracksTextView = true 110 | return textView 111 | }() 112 | } 113 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/AppKit/TargetDragView/AxcTargetDragView+Api.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcTargetDragView+Api.swift 3 | // AxcUIKit 4 | // 5 | // Created by 赵新 on 26/9/2023. 6 | // 7 | 8 | import AxcBedrock 9 | 10 | // MARK: - [AxcTargetDragViewApi] 11 | 12 | public protocol AxcTargetDragViewApi { } 13 | 14 | extension AxcTargetDragViewApi where Self: AxcTargetDragView { } 15 | 16 | // MARK: - AxcTargetDragView + AxcUICallbackTarget 17 | 18 | public extension AxcUICallback where Base: AxcTargetDragView { 19 | /// 设置拖拽结束的回调 20 | func targetDraggingEnd(_ block: @escaping AxcBlock.OneParam<[URL]>) { 21 | base._targetDraggingEndBlock = block 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/AppKit/TargetDragView/AxcTargetDragView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcFileDragView.swift 3 | // AxcUIKit 4 | // 5 | // Created by 赵新 on 26/9/2023. 6 | // 7 | 8 | import Cocoa 9 | import AxcBedrock 10 | 11 | extension AxcTargetDragView { 12 | /// set focus view, and set hidden is true, add view, make constraints edge is fill 13 | /// 设置焦点视图 14 | func _set(focusView: NSView) { 15 | _focusView?.removeFromSuperview() // remove 16 | _focusView = focusView // set 17 | _focusView?.isHidden = true // hidden 18 | if let _focusView { // unpack is no optional 19 | addSubview(focusView) // add 20 | _focusView.snp.makeConstraints { $0.edges.equalToSuperview() } 21 | } 22 | } 23 | 24 | /// 设置焦点视图是否隐藏 25 | func _setFocusView(isHidden: Bool) { 26 | _focusView?.isHidden = isHidden 27 | } 28 | } 29 | 30 | // MARK: - [AxcTargetDragView] 31 | 32 | open class AxcTargetDragView: AxcView { 33 | /// 全事件穿透 34 | open override func hitTest(_ point: NSPoint) -> NSView? { 35 | return nil 36 | } 37 | 38 | /// 该方法会在拖动进入当前视图或窗口的时候调用,并给予(即当前视图或窗口)一个机会来决定是否接受该拖动操作。 39 | /// 该方法的参数包含了一个NSDraggingInfo实例,提供了关于拖动内容的一些信息,包括拖动数据的类型、数据大小、拖动源、拖动起始位置等。 40 | /// 可以通过该方法的返回值来控制是否接受这个拖动操作。 41 | open override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { 42 | _setFocusView(isHidden: false) 43 | return .copy 44 | } 45 | 46 | /// 该方法会在拖动操作离开当前视图或窗口的时候调用,可以根据需要执行一些清理操作。 47 | open override func draggingExited(_ sender: NSDraggingInfo?) { 48 | _setFocusView(isHidden: true) 49 | } 50 | 51 | /// 该方法会在目标视图或窗口接收到拖动操作并且鼠标移动时不断调用,可以根据需要更新视图或做出其他响应。 52 | open override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation { 53 | return .copy 54 | } 55 | 56 | /// 该方法会在用户释放鼠标按钮并且拖动操作将被执行时调用,可以执行一些准备工作,如更新视图的状态、替换拖动数据等。 57 | open override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool { 58 | return true 59 | } 60 | 61 | /// 该方法会在用户释放鼠标按钮并且拖动操作将被执行时调用,并且应该执行最终的操作,如将数据插入目标视图、保存数据等。 62 | open override func performDragOperation(_ sender: NSDraggingInfo) -> Bool { 63 | let urls = _draggingInfoFileUrl(sender) 64 | _targetDraggingEndBlock?(urls) 65 | return true 66 | } 67 | 68 | /// 该方法会在拖动操作完成并在执行performDragOperation:方法后立即调用,可以根据需要执行一些收尾操作。 69 | open override func concludeDragOperation(_ sender: NSDraggingInfo?) { 70 | _setFocusView(isHidden: true) 71 | } 72 | 73 | open override func config() { 74 | super.config() 75 | } 76 | 77 | open override func makeUI() { 78 | super.makeUI() 79 | set(backgroundColor: NSColor.clear) 80 | registerForDraggedTypes([ 81 | .fileURL, 82 | ]) 83 | } 84 | 85 | /// 获取拖动文件的URL地址 86 | private func _draggingInfoFileUrl(_ sender: NSDraggingInfo) -> [URL] { 87 | let pasteboard = sender.draggingPasteboard 88 | // 在此方法中读取剪贴板中的数据,并处理传输过来的文件 89 | guard let urls = pasteboard.readObjects(forClasses: [NSURL.self], options: nil) as? [URL] else { return [] } 90 | return urls 91 | } 92 | 93 | /// 执行拖拽后的回调 94 | var _targetDraggingEndBlock: AxcBlock.OneParam<[URL]>? 95 | 96 | /// If this view show is when mouse touch down and drag file into `AxcFileDragView` 97 | /// 焦点 98 | private var _focusView: NSView? 99 | } 100 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/Core/AxcUIKitLib.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcUIKitLib.swift 3 | // AxcUIKit-Swift 4 | // 5 | // Created by 赵新 on 2023/6/21. 6 | // 7 | 8 | import AxcBedrock 9 | 10 | // MARK: - [AxcUIKitLib] 11 | 12 | open class AxcUIKitLib: NSObject, AxcLibraryTarget { 13 | public var moduleName: String { 14 | return "AxcUIKit" 15 | } 16 | 17 | public var moduleEmoji: String? { 18 | return "🧱" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/Core/Protocol/AxcUIBasicFuncTarget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcUIBasicFuncTarget.swift 3 | // AxcBedrock 4 | // 5 | // Created by 赵新 on 2023/6/22. 6 | // 7 | 8 | import AxcBedrock 9 | 10 | // MARK: - [AxcUIBasicFuncTarget] 11 | 12 | @objc 13 | public protocol AxcUIBasicFuncTarget: NSObjectProtocol { 14 | /** 15 | Write initialize config code in this function 16 | 配置数据的地方 17 | */ 18 | @objc 19 | func config() 20 | 21 | /** 22 | Write User Interface code in this function 23 | add view layout direction need form top to bottom and left to right 24 | 创建UI的方法放在这里 25 | 最好按照,从上至下,从左至右依次添加视图 26 | */ 27 | @objc 28 | func makeUI() 29 | 30 | /** 31 | Write data request or load code in this function 32 | data即将加载 33 | */ 34 | @objc 35 | func dataWillLoad() 36 | 37 | /** 38 | Write view action event input code in this function 39 | Data flow is forward direction 40 | 正向数据流(页面操作的监听)方法放在这里 41 | */ 42 | @objc 43 | func bindViewAction() 44 | 45 | /** 46 | Write ViewModel output code in this function 47 | Data flow is reverse direction 48 | 反向数据流(ViewModel的监听)进行数据绑定的放在这里 49 | */ 50 | @objc 51 | func bindViewModel() 52 | 53 | /** 54 | Write Notification input code in this function 55 | Data flow is from notification 56 | 通知数据流,主要用于接口暴露和复用 57 | */ 58 | @objc 59 | func bindNotice() 60 | 61 | /** 62 | Write Notification input code in this function 63 | Data flow is from outside invoking 64 | 驱动数据流,主要用于外部驱动 65 | */ 66 | @objc 67 | func bindDrive() 68 | } 69 | 70 | public extension AxcUIBasicFuncTarget { 71 | /// Perform basic function(`config` and `makeUI` function) 72 | /// 执行基础方法 73 | func performBasic() { 74 | config() 75 | makeUI() 76 | } 77 | 78 | /// Perform data flow function(all prefix is `bind` and `dataWillLoad` function) 79 | /// 执行数据通道方法 80 | func performDataChannel() { 81 | // Filter 82 | guard !axc_isFirstPerformDataChannel else { return } 83 | // Just perform onice 84 | // 标记为只执行一次 85 | axc_isFirstPerformDataChannel = true 86 | bindViewModel() // 反向数据流绑定 87 | bindViewAction() // 正向数据流绑定 88 | bindNotice() // 协议通知数据流绑定 89 | bindDrive() // 协议通知数据流绑定 90 | dataWillLoad() // 绑定完后再执行数据加载 91 | } 92 | } 93 | 94 | private var k_isFirstPerformDataChannel = "k_isFirstPerformDataChannel" 95 | 96 | private extension AxcUIBasicFuncTarget { 97 | /// Records whether the view was added for the first time 98 | /// 用于记录是否是第一次添加进视图 99 | var axc_isFirstPerformDataChannel: Bool { 100 | set { AxcRuntime.Set(object: self, key: &k_isFirstPerformDataChannel, value: newValue, policy: .OBJC_ASSOCIATION_ASSIGN) } 101 | get { 102 | guard let value: Bool = AxcRuntime.GetObject(self, key: &k_isFirstPerformDataChannel) else { 103 | let value: Bool = false 104 | self.axc_isFirstPerformDataChannel = value 105 | return value 106 | } 107 | return value 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/Core/Protocol/AxcUICallbackTarget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcUICallbackTarget.swift 3 | // AxcUIKit 4 | // 5 | // Created by 赵新 on 2023/6/22. 6 | // 7 | 8 | // MARK: - [AxcUICallbackTarget] 9 | 10 | public protocol AxcUICallbackTarget { } 11 | public extension AxcUICallbackTarget { 12 | /// 命名空间, 实例类型 13 | var callback: AxcUICallback { 14 | set { } 15 | get { return AxcUICallback(self) } 16 | } 17 | } 18 | 19 | // MARK: - [AxcUICallback] 20 | 21 | open class AxcUICallback { 22 | public init(_ base: Base) { 23 | self.base = base 24 | } 25 | 26 | public var base: Base 27 | } 28 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/Basic/View/AxcView+Api.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcView+Api.swift 3 | // AxcUIKit 4 | // 5 | // Created by 赵新 on 2023/6/22. 6 | // 7 | 8 | import AxcBedrock 9 | 10 | // MARK: - [AxcViewApi] 11 | 12 | public protocol AxcViewApi { 13 | /// (💈跨平台标识)获取颜色 14 | var currentBackgroundColor: AxcBedrockColor? { get } 15 | 16 | /// (💈跨平台标识)当前图层 17 | var currentLayer: CALayer? { get } 18 | } 19 | 20 | public extension AxcViewApi where Self: AxcView { 21 | /// (💈跨平台标识)设置颜色 22 | func set(backgroundColor: AxcUnifiedColor?) { 23 | _set(backgroundColor: backgroundColor) 24 | } 25 | 26 | /// (💈跨平台标识)检查视图的布局或约束是否固定了某个属性 27 | /// - Parameter firstAttribute: 检查固定的属性 28 | /// - Returns: 结果 29 | func isLayoutEqualConstant(firstAttribute: NSLayoutConstraint.Attribute) -> Bool { 30 | _isLayoutEqualConstant(firstAttribute: firstAttribute) 31 | } 32 | 33 | /// (💈跨平台标识)检查视图的布局或约束是否固定了大小 34 | func isLayoutFixedSize() -> Bool { 35 | _isLayoutFixedSize() 36 | } 37 | 38 | /// (💈跨平台标识)检查视图的布局或约束是否固定了宽度 39 | func isLayoutFixedWidth() -> Bool { 40 | _isLayoutEqualConstant(firstAttribute: .width) 41 | } 42 | 43 | /// (💈跨平台标识)检查视图的布局或约束是否固定了高度 44 | func isLayoutFixedHeight() -> Bool { 45 | _isLayoutEqualConstant(firstAttribute: .height) 46 | } 47 | } 48 | 49 | // MARK: - AxcView + AxcUICallbackTarget 50 | 51 | extension AxcView: AxcUICallbackTarget { } 52 | 53 | // MARK: - 通用兼容 54 | 55 | #if os(macOS) 56 | public extension AxcView { 57 | typealias HitTestBlock = (_ view: AxcView, 58 | _ superBack: NSView?, 59 | _ point: NSPoint) -> NSView? 60 | } 61 | 62 | public extension AxcUICallback where Base: AxcView { } 63 | 64 | #elseif os(iOS) || os(tvOS) || os(watchOS) 65 | public extension AxcView { 66 | typealias HitTestBlock = (_ view: AxcView, 67 | _ superBack: UIView?, 68 | _ point: CGPoint, 69 | _ event: UIEvent?) -> UIView? 70 | } 71 | 72 | #endif 73 | public extension AxcUICallback where Base: AxcView { 74 | /// 布局子视图 75 | /// - Parameter block: 回调 76 | func layoutSubviews(_ block: @escaping AxcBlock.Empty) { 77 | base._layoutSubviewsBlock = block 78 | } 79 | 80 | /// 触发了点击判定 81 | /// - Parameter block: 回调 82 | func hitTest(_ block: @escaping AxcView.HitTestBlock) { 83 | base._hitTestBlock = block 84 | } 85 | } 86 | 87 | // MARK: - iOS独有 88 | 89 | #if os(iOS) || os(tvOS) || os(watchOS) 90 | 91 | public extension AxcView { 92 | typealias PointInsetBlock = (_ view: AxcView, 93 | _ superBack: Bool, 94 | _ point: CGPoint, 95 | _ event: UIEvent?) -> Bool 96 | } 97 | 98 | public extension AxcUICallback where Base: AxcView { 99 | /// 点位是否在视图内 100 | /// - Parameter block: 回调 101 | func pointInside(_ block: @escaping AxcView.PointInsetBlock) { 102 | base._pointInsideBlock = block 103 | } 104 | } 105 | #endif 106 | 107 | // MARK: - MacOS独有 108 | 109 | #if os(macOS) 110 | #endif 111 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/Basic/View/AxcView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcView.swift 3 | // AxcUIKit 4 | // 5 | // Created by 赵新 on 2023/6/22. 6 | // 7 | 8 | import AxcBedrock 9 | 10 | #if os(macOS) 11 | import AppKit 12 | 13 | public typealias AxcSystemBaseView = NSView 14 | 15 | #elseif os(iOS) || os(tvOS) || os(watchOS) 16 | import UIKit 17 | 18 | public typealias AxcSystemBaseView = UIView 19 | 20 | #endif 21 | 22 | extension AxcSystemBaseView { 23 | /// 跨平台系统基类设置背景颜色 24 | var _axc_backgroundColor: AxcBedrockColor? { 25 | set { 26 | let color = AxcBedrockColor.Axc.CreateOptional(newValue) ?? .white 27 | #if os(macOS) 28 | layer?.backgroundColor = color.cgColor 29 | #elseif os(iOS) || os(tvOS) || os(watchOS) 30 | backgroundColor = color 31 | #endif 32 | } 33 | get { 34 | #if os(macOS) 35 | return layer?.backgroundColor?.axc.nsColor 36 | #elseif os(iOS) || os(tvOS) || os(watchOS) 37 | return backgroundColor 38 | #endif 39 | } 40 | } 41 | } 42 | 43 | // MARK: - AxcView + AxcUIBasicFuncTarget 44 | 45 | extension AxcView: AxcUIBasicFuncTarget { } 46 | 47 | // MARK: - AxcView + AxcViewApi 48 | 49 | extension AxcView: AxcViewApi { 50 | /// (💈跨平台标识)获取颜色 51 | public var currentBackgroundColor: AxcBedrockColor? { 52 | return _axc_backgroundColor 53 | } 54 | 55 | /// (💈跨平台标识)获取图层 56 | public var currentLayer: CALayer? { 57 | return layer 58 | } 59 | } 60 | 61 | extension AxcView { 62 | func _set(backgroundColor: AxcUnifiedColor?) { 63 | let color = AxcBedrockColor.Axc.CreateOptional(backgroundColor) ?? .white 64 | _axc_backgroundColor = color 65 | } 66 | 67 | func _isLayoutEqualConstant(firstAttribute: NSLayoutConstraint.Attribute) -> Bool { 68 | // Auto Layout 约束固定的情况 69 | for constraint in constraints { 70 | if constraint.priority == .required, // 必须满足的约束 71 | constraint.firstAttribute == firstAttribute, // 条件 72 | constraint.relation == .equal, // 匹配 73 | constraint.constant > 0 { // 比零大 74 | return true 75 | } 76 | } 77 | return false // 没有固定的约束 78 | } 79 | 80 | func _isLayoutFixedSize() -> Bool { 81 | let isFixedWidth = _isLayoutEqualConstant(firstAttribute: .width) 82 | let isFixedHeight = _isLayoutEqualConstant(firstAttribute: .height) 83 | return isFixedWidth && isFixedHeight 84 | } 85 | } 86 | 87 | // MARK: - [AxcView] 88 | 89 | open class AxcView: AxcSystemBaseView { 90 | public required convenience init() { 91 | self.init(frame: .zero) 92 | } 93 | 94 | @available(*, unavailable) 95 | public required init?(coder: NSCoder) { 96 | super.init(coder: coder) 97 | } 98 | 99 | public override init(frame: CGRect) { 100 | super.init(frame: frame) 101 | performBasic() 102 | } 103 | 104 | // MARK: 通用方法-有对应/替代的Api 105 | 106 | /// 点击触发回调 107 | var _hitTestBlock: HitTestBlock? 108 | 109 | #if os(macOS) 110 | 111 | open override func hitTest(_ point: NSPoint) -> NSView? { 112 | let superView = super.hitTest(point) 113 | if let _hitTestBlock { 114 | return _hitTestBlock(self, superView, point) 115 | } else { 116 | return superView 117 | } 118 | } 119 | 120 | #elseif os(iOS) || os(tvOS) || os(watchOS) 121 | /// 点击判断事件 122 | open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 123 | let superView = super.hitTest(point, with: event) 124 | return _hitTestBlock?(self, superView, point, event) 125 | } 126 | 127 | #endif 128 | 129 | /// 布局子视图回调 130 | var _layoutSubviewsBlock: AxcBlock.Empty? 131 | 132 | // MARK: 跨平台兼容-需要支持跨平台重写的 133 | 134 | /// 设置图层类型 135 | open class var Axc_layerClass: CALayer.Type { 136 | return AxcLayer.self 137 | } 138 | 139 | /// 已经移动到父视图 140 | open func axc_didMoveToSuperview() { 141 | #if os(macOS) 142 | super.viewDidMoveToSuperview() 143 | #elseif os(iOS) || os(tvOS) || os(watchOS) 144 | super.didMoveToSuperview() 145 | #endif 146 | performDataChannel() 147 | } 148 | 149 | /// 布局子视图回调 150 | open func axc_layoutSubviews() { 151 | #if os(macOS) 152 | super.layout() 153 | #elseif os(iOS) || os(tvOS) || os(watchOS) 154 | super.layoutSubviews() 155 | #endif 156 | _layoutSubviewsBlock?() 157 | } 158 | 159 | /* 原方法需要禁用重写 */ 160 | #if os(macOS) 161 | 162 | public final override func viewDidMoveToSuperview() { 163 | axc_didMoveToSuperview() 164 | } 165 | 166 | public final override func layout() { 167 | axc_layoutSubviews() 168 | } 169 | 170 | #elseif os(iOS) || os(tvOS) || os(watchOS) 171 | 172 | public final override class var layerClass: AnyClass { 173 | return Axc_layerClass 174 | } 175 | 176 | public final override func didMoveToSuperview() { 177 | axc_didMoveToSuperview() 178 | } 179 | 180 | public final override func layoutSubviews() { 181 | axc_layoutSubviews() 182 | } 183 | #endif 184 | 185 | // MARK: iOS独有 186 | 187 | #if os(iOS) || os(tvOS) || os(watchOS) 188 | /// 点位是否在视图内 189 | var _pointInsideBlock: PointInsetBlock? 190 | /// 点位判断事件 191 | open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { 192 | let superPoint = super.point(inside: point, with: event) 193 | return _pointInsideBlock?(self, superPoint, point, event) ?? superPoint 194 | } 195 | 196 | /// 禁用重写 197 | @available(*, unavailable) 198 | public final override var backgroundColor: UIColor? { 199 | set { } 200 | get { return currentBackgroundColor } 201 | } 202 | 203 | #endif 204 | 205 | // MARK: MacOS独有 206 | 207 | #if os(macOS) 208 | #endif 209 | 210 | // MARK: 基础方法 211 | 212 | /// 配置数据的地方 213 | open func config() { 214 | #if os(macOS) 215 | /* 216 | 在Cocoa框架中,wantsLayer是NSView类中的一个布尔属性。 217 | 如果将NSView的wantsLayer属性设置为YES,那么这个NSView就会有一个CALayer实例作为其backing layer。 218 | 219 | 简单来说,wantsLayer是用来开启Core Animation的支持的 220 | Core Animation是一种动画渲染方式,通过OpenGL渲染引擎来让应用程序中的层进行动画渲染。 221 | 如果在Mac应用程序中需要使用动画效果,就需要开启wantsLayer属性,然后在layer中设置动画效果,这样就能够实现高效的动态界面渲染了。 222 | 223 | 在使用wantsLayer属性时,一定要确保在视图层级中的所有视图wantsLayer属性都被设置为YES,这样才能在整个视图层级中启用Core Animation渲染引擎。 224 | */ 225 | wantsLayer = true 226 | layer = Self.Axc_layerClass.init() 227 | #elseif os(iOS) || os(tvOS) || os(watchOS) 228 | super.backgroundColor = UIColor.white 229 | #endif 230 | translatesAutoresizingMaskIntoConstraints = false 231 | } 232 | 233 | /// 创建UI的方法放在这里。按照,从上至下,从左至右依次添加视图 234 | open func makeUI() { } 235 | 236 | /// 数据即将刷新 237 | open func dataWillLoad() { } 238 | 239 | /// 正向数据流(页面操作的监听)方法放在这里 240 | open func bindViewAction() { } 241 | 242 | /// 反向数据流(ViewModel的监听)进行数据绑定的放在这里 243 | open func bindViewModel() { } 244 | 245 | /// 通知数据流,主要用于接口暴露和复用 246 | open func bindNotice() { } 247 | 248 | /// 驱动数据流,主要用于外部驱动 249 | open func bindDrive() { } 250 | } 251 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/Basic/ViewController/AxcViewController+Api.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcViewController+Api.swift 3 | // AxcUIKit 4 | // 5 | // Created by 赵新 on 26/9/2023. 6 | // 7 | 8 | import AxcBedrock 9 | 10 | // MARK: - [AxcViewControllerApi] 11 | 12 | public protocol AxcViewControllerApi { 13 | /// (💈跨平台标识)获取颜色 14 | var viewBackgroundColor: AxcBedrockColor? { get } 15 | 16 | /// (💈跨平台标识)获取图层 17 | var axcView: AxcView? { get } 18 | } 19 | 20 | public extension AxcViewControllerApi where Self: AxcViewController { 21 | /// (💈跨平台标识)设置View颜色 22 | func setView(backgroundColor: AxcUnifiedColor?) { 23 | _setView(backgroundColor: backgroundColor) 24 | } 25 | } 26 | 27 | // MARK: - AxcViewController + AxcUICallbackTarget 28 | 29 | extension AxcViewController: AxcUICallbackTarget { } 30 | 31 | public extension AxcUICallback where Base: AxcViewController { } 32 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/Basic/ViewController/AxcViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcViewController.swift 3 | // AxcUIKit-Swift 4 | // 5 | // Created by 赵新 on 2023/6/21. 6 | // 7 | 8 | import AxcBedrock 9 | 10 | #if os(macOS) 11 | import AppKit 12 | 13 | public typealias AxcSystemBaseViewController = NSViewController 14 | 15 | #elseif os(iOS) || os(tvOS) || os(watchOS) 16 | import UIKit 17 | 18 | public typealias AxcSystemBaseViewController = UIViewController 19 | 20 | #endif 21 | 22 | // MARK: - AxcViewController + AxcUIBasicFuncTarget 23 | 24 | extension AxcViewController: AxcUIBasicFuncTarget { } 25 | 26 | // MARK: - AxcViewController + AxcViewControllerApi 27 | 28 | extension AxcViewController: AxcViewControllerApi { 29 | /// (💈跨平台标识)获取View颜色 30 | public var viewBackgroundColor: AxcBedrockColor? { 31 | if let axcView { // is AxcView 32 | return axcView.currentBackgroundColor 33 | } else { // no AxcView 34 | return view._axc_backgroundColor 35 | } 36 | } 37 | 38 | /// (💈跨平台标识)获取AxcView 39 | public var axcView: AxcView? { 40 | return view as? AxcView 41 | } 42 | } 43 | 44 | extension AxcViewController { 45 | /// 设置背景颜色 46 | func _setView(backgroundColor: AxcUnifiedColor?) { 47 | if let axcView { // is AxcView 48 | axcView.set(backgroundColor: backgroundColor) 49 | } else { // no AxcView 50 | let color = AxcBedrockColor.Axc.CreateOptional(backgroundColor) ?? .white 51 | view._axc_backgroundColor = color 52 | } 53 | } 54 | } 55 | 56 | // MARK: - [AxcViewController] 57 | 58 | open class AxcViewController: AxcSystemBaseViewController { 59 | // MARK: 父类重写 60 | 61 | open override func loadView() { 62 | #if os(macOS) 63 | /* 64 | 在 macOS 10.10 及更高版本中,loadView() 方法会自动查找与视图控制器同名的 nib 文件。 65 | 要利用此行为,请在其相应的视图控制器之后命名一个 nib 文件,并将 nil 传递给 init(nibName:bundle:) 方法的两个参数。 66 | 您可以通过使用空实现覆盖该方法来确认此行为 loadView()——这将退出该默认行为。 67 | */ 68 | view = AxcView(frame: .zero) 69 | #elseif os(iOS) || os(tvOS) || os(watchOS) 70 | super.loadView() 71 | #endif 72 | } 73 | 74 | open override func viewDidLoad() { 75 | super.viewDidLoad() 76 | performBasic() 77 | performDataChannel() 78 | } 79 | 80 | /// 添加视图 81 | open func addSubview(_ view: AxcBedrockView) { 82 | self.view.addSubview(view) 83 | } 84 | 85 | // MARK: 子类重写 86 | 87 | /// 配置数据的地方 88 | open func config() { } 89 | 90 | /// 创建UI的方法放在这里。按照,从上至下,从左至右依次添加视图 91 | open func makeUI() { } 92 | 93 | /// 数据即将刷新 94 | open func dataWillLoad() { } 95 | 96 | /// 正向数据流(页面操作的监听)方法放在这里 97 | open func bindViewAction() { } 98 | 99 | /// 反向数据流(ViewModel的监听)进行数据绑定的放在这里 100 | open func bindViewModel() { } 101 | 102 | /// 通知数据流,主要用于接口暴露和复用 103 | open func bindNotice() { } 104 | 105 | /// 驱动数据流,主要用于外部驱动 106 | open func bindDrive() { } 107 | } 108 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/BubbleView/AxcBubbleView+Api.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcBubbleView+Api.swift 3 | // Kingfisher 4 | // 5 | // Created by 赵新 on 2023/5/31. 6 | // 7 | 8 | import AxcBedrock 9 | 10 | // MARK: - [AxcBubbleView.ArrowDirection] 11 | 12 | public extension AxcBubbleView { 13 | /// 箭头方向以及偏移量 14 | enum ArrowDirection { 15 | /// 上,和水平偏移量 16 | case top(offsetX: CGFloat) 17 | /// 上居中 18 | case topCenter 19 | 20 | /// 左,和垂直偏移量 21 | case left(offsetY: CGFloat) 22 | /// 左居中 23 | case leftCenter 24 | 25 | /// 下,和水平偏移量 26 | case bottom(offsetX: CGFloat) 27 | /// 下居中 28 | case bottomCenter 29 | 30 | /// 右,和垂直偏移量 31 | case right(offsetY: CGFloat) 32 | /// 右居中 33 | case rightCenter 34 | } 35 | } 36 | 37 | // MARK: - [AxcBubbleViewApi] 38 | 39 | public protocol AxcBubbleViewApi { 40 | /// 内容间距 41 | var contentEdgeInsets: AxcBedrockEdgeInsets { get } 42 | 43 | /// 内容视图,需要添加的内容可以加在这个视图上 44 | var contentView: AxcGradientView { get } 45 | 46 | /// 箭头位置 47 | var arrowDirection: AxcBubbleView.ArrowDirection { get } 48 | /// 箭头大小 49 | var arrowSize: CGSize { get } 50 | /// 箭头圆角半径 51 | var arrowRadius: CGFloat { get } 52 | 53 | /// 气泡内容间距 54 | var bubbleContentEdgeInsets: AxcBedrockEdgeInsets { get } 55 | /// 气泡圆角组 56 | var bubbleCorners: AxcCorner { get } 57 | /// 气泡圆角半径 58 | var bubbleCornerRadius: CGFloat { get } 59 | } 60 | 61 | public extension AxcBubbleViewApi where Self: AxcBubbleView { 62 | /// 设置内容间距 63 | /// - Parameter contentEdgeInsets: 内容间距 64 | func set(contentEdgeInsets: AxcBedrockEdgeInsets) { 65 | _set(contentEdgeInsets: contentEdgeInsets) 66 | } 67 | 68 | /// 设置箭头位置 69 | /// - Parameter arrowDirection: 箭头位置 70 | func set(arrowDirection: AxcBubbleView.ArrowDirection) { 71 | _set(arrowDirection: arrowDirection) 72 | } 73 | 74 | /// 设置箭头大小 75 | /// - Parameter arrowSize: 箭头大小 76 | func set(arrowSize: CGSize) { 77 | _set(arrowSize: arrowSize) 78 | } 79 | 80 | /// 设置箭头圆角半径 81 | /// - Parameter arrowRadius: 箭头圆角半径 82 | func set(arrowRadius: CGFloat) { 83 | _set(arrowRadius: arrowRadius) 84 | } 85 | 86 | /// 设置气泡内容间距 87 | /// - Parameter bubbleContentEdgeInsets: 气泡内容间距 88 | func set(bubbleContentEdgeInsets: AxcBedrockEdgeInsets) { 89 | _set(bubbleContentEdgeInsets: bubbleContentEdgeInsets) 90 | } 91 | 92 | /// 设置气泡圆角组 93 | /// - Parameter bubbleCorners: 气泡圆角组 94 | func set(bubbleCorners: AxcCorner) { 95 | _set(bubbleCorners: bubbleCorners) 96 | } 97 | 98 | /// 设置气泡圆角半径 99 | /// - Parameter bubbleCornerRadius: 气泡圆角半径 100 | func set(bubbleCornerRadius: CGFloat) { 101 | _set(bubbleCornerRadius: bubbleCornerRadius) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/BubbleView/AxcBubbleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcBubbleView.swift 3 | // Kingfisher 4 | // 5 | // Created by 赵新 on 2023/5/31. 6 | // 7 | 8 | import SnapKit 9 | import AxcBedrock 10 | 11 | // MARK: - AxcBubbleView + AxcBubbleViewApi 12 | 13 | extension AxcBubbleView: AxcBubbleViewApi { 14 | public var contentEdgeInsets: AxcBedrockEdgeInsets { 15 | return _contentEdgeInsets 16 | } 17 | 18 | public var contentView: AxcGradientView { 19 | return _contentView 20 | } 21 | 22 | public var arrowDirection: ArrowDirection { 23 | return _arrowDirection 24 | } 25 | 26 | public var arrowSize: CGSize { 27 | return _arrowSize 28 | } 29 | 30 | public var arrowRadius: CGFloat { 31 | return _arrowRadius 32 | } 33 | 34 | public var bubbleContentEdgeInsets: AxcBedrockEdgeInsets { 35 | return _bubbleContentEdgeInsets 36 | } 37 | 38 | public var bubbleCorners: AxcCorner { 39 | return _bubbleCorners 40 | } 41 | 42 | public var bubbleCornerRadius: CGFloat { 43 | return _bubbleCornerRadius 44 | } 45 | } 46 | 47 | extension AxcBubbleView { 48 | func _set(contentEdgeInsets: AxcBedrockEdgeInsets) { 49 | _contentEdgeInsets = contentEdgeInsets 50 | _updateMaskLayer() 51 | _updateContentView() 52 | } 53 | 54 | func _set(arrowDirection: AxcBubbleView.ArrowDirection) { 55 | _arrowDirection = arrowDirection 56 | _updateMaskLayer() 57 | _updateContentView() 58 | } 59 | 60 | func _set(arrowSize: CGSize) { 61 | _arrowSize = arrowSize 62 | _updateMaskLayer() 63 | _updateContentView() 64 | } 65 | 66 | func _set(arrowRadius: CGFloat) { 67 | _arrowRadius = arrowRadius 68 | _updateMaskLayer() 69 | } 70 | 71 | func _set(bubbleContentEdgeInsets: AxcBedrockEdgeInsets) { 72 | _bubbleContentEdgeInsets = bubbleContentEdgeInsets 73 | _updateMaskLayer() 74 | _updateContentView() 75 | } 76 | 77 | func _set(bubbleCorners: AxcCorner) { 78 | _bubbleCorners = bubbleCorners 79 | _updateMaskLayer() 80 | } 81 | 82 | func _set(bubbleCornerRadius: CGFloat) { 83 | _bubbleCornerRadius = bubbleCornerRadius 84 | _updateMaskLayer() 85 | _updateContentView() 86 | } 87 | } 88 | 89 | // MARK: - [AxcBubbleView] 90 | 91 | open class AxcBubbleView: AxcGradientView { 92 | open override func axc_layoutSubviews() { 93 | super.axc_layoutSubviews() 94 | _updateMaskLayer() 95 | } 96 | 97 | open override func makeUI() { 98 | super.makeUI() 99 | set(backgroundColor: "000000") 100 | 101 | addSubview(contentView) 102 | _updateContentView() 103 | } 104 | 105 | func _updateContentView() { 106 | let distanceContent = _contentEdgeInsets.axc.add(edge: _bubbleContentEdgeInsets) 107 | var topDistance: CGFloat = distanceContent.top 108 | var leftDistance: CGFloat = distanceContent.left 109 | var bottomDistance: CGFloat = -distanceContent.bottom 110 | var rightDistance: CGFloat = -distanceContent.right 111 | switch _arrowDirection { 112 | case .top, .topCenter: 113 | topDistance += _arrowSize.height 114 | case .left, .leftCenter: 115 | leftDistance += _arrowSize.width 116 | case .bottom, .bottomCenter: 117 | bottomDistance += -_arrowSize.height 118 | case .right, .rightCenter: 119 | rightDistance += -_arrowSize.width 120 | } 121 | contentView.snp.remakeConstraints { make in 122 | make.top.equalTo(topDistance) 123 | make.left.equalTo(leftDistance) 124 | make.bottom.equalTo(bottomDistance) 125 | make.right.equalTo(rightDistance) 126 | } 127 | } 128 | 129 | func _updateMaskLayer() { 130 | guard frame != .zero else { return } 131 | // 一半大小,用于减轻运算次数 132 | let halfArrowSize: CGSize = .init(width: _arrowSize.width / 2, height: _arrowSize.height / 2) 133 | // 箭头偏移限位边距 134 | let arrowOffsetEdgeLimit: AxcBedrockEdgeInsets = _contentEdgeInsets.axc.add(size: _bubbleCornerRadius.axc.cgSize) 135 | // 根据方向留出空位 136 | var contentRectInsetEdge: AxcBedrockEdgeInsets = .Axc.Create(all: 0) // 箭头的空位 137 | var arrowRoundCenter: CGPoint = .zero // 箭头顶部圆的中心点 138 | var startAngleRadian: CGFloat = 0 // 箭头顶部圆的起始角度 139 | var endAngleRadian: CGFloat = 0 // 箭头顶部圆的终止角度 140 | var clockwise: Bool = true // 顺逆时针 141 | 142 | var startPoint: CGPoint = .zero 143 | var endPoint: CGPoint = .zero 144 | switch _arrowDirection { 145 | case let .top(offsetX: offsetX): createVertical(offsetX: offsetX, vertical: .top) 146 | case .topCenter: createVertical(offsetX: (frame.width - _arrowSize.width) / 2, vertical: .top) 147 | 148 | case let .left(offsetY: offsetY): createHorizontal(offsetY: offsetY, horizontal: .left) 149 | case .leftCenter: createHorizontal(offsetY: (frame.height - _arrowSize.height) / 2, horizontal: .left) 150 | 151 | case let .bottom(offsetX: offsetX): createVertical(offsetX: offsetX, vertical: .bottom) 152 | case .bottomCenter: createVertical(offsetX: (frame.width - _arrowSize.width) / 2, vertical: .bottom) 153 | 154 | case let .right(offsetY: offsetY): createHorizontal(offsetY: offsetY, horizontal: .right) 155 | case .rightCenter: createHorizontal(offsetY: (frame.height - _arrowSize.height) / 2, horizontal: .right) 156 | } 157 | 158 | // 构建垂直参数 159 | func createVertical(offsetX: CGFloat, vertical: AxcDirectionVertical) { 160 | let arrowHalfRadian: CGFloat = atan2(halfArrowSize.width, _arrowSize.height) // 等腰三角内角角度 161 | let arrowInteriorAngle = 90 - arrowHalfRadian.axc.radianToAngle // 同角三角形的内角相等 162 | var arrowRoundCenterX = offsetX + halfArrowSize.width 163 | // 设限阈值 164 | let less = _contentEdgeInsets.left + _bubbleCornerRadius + halfArrowSize.width 165 | let greater = frame.width - _contentEdgeInsets.right - _bubbleCornerRadius - halfArrowSize.width 166 | arrowRoundCenterX = arrowRoundCenterX.axc.limitThan(min: less, 167 | max: greater) 168 | switch vertical { 169 | case .top: 170 | contentRectInsetEdge = _arrowSize.height.axc.edgeInsetsTop 171 | arrowRoundCenter = .init(x: arrowRoundCenterX, y: _arrowRadius + _contentEdgeInsets.top) 172 | startAngleRadian = (-90 - arrowInteriorAngle).axc.angleToRadian 173 | endAngleRadian = (-90 + arrowInteriorAngle).axc.angleToRadian 174 | clockwise = true 175 | startPoint = CGPoint(x: offsetX + _arrowSize.width, 176 | y: _arrowSize.height + _contentEdgeInsets.top) 177 | 178 | case .bottom: 179 | contentRectInsetEdge = _arrowSize.height.axc.edgeInsetsBottom 180 | arrowRoundCenter = .init(x: arrowRoundCenterX, y: frame.height - _arrowRadius - _contentEdgeInsets.bottom) 181 | startAngleRadian = (-270 + arrowInteriorAngle).axc.angleToRadian 182 | endAngleRadian = (-270 - arrowInteriorAngle).axc.angleToRadian 183 | clockwise = false 184 | startPoint = CGPoint(x: offsetX + _arrowSize.width, 185 | y: frame.height - _arrowSize.height - _contentEdgeInsets.bottom) 186 | } 187 | endPoint = CGPoint(x: offsetX, y: startPoint.y) 188 | // 设置水平限位 189 | startPoint.x = startPoint.x.axc.limitThan(min: arrowOffsetEdgeLimit.left + _arrowSize.width, 190 | max: frame.width - arrowOffsetEdgeLimit.right) 191 | endPoint.x = endPoint.x.axc.limitThan(min: arrowOffsetEdgeLimit.left, 192 | max: frame.width - arrowOffsetEdgeLimit.right - _arrowSize.width) 193 | } 194 | 195 | // 构建水平参数 196 | func createHorizontal(offsetY: CGFloat, horizontal: AxcDirectionHorizontal) { 197 | let arrowHalfRadian: CGFloat = atan2(halfArrowSize.height, _arrowSize.width) // 等腰三角内角角度 198 | let arrowInteriorAngle = 90 - arrowHalfRadian.axc.radianToAngle // 同角三角形的内角相等 199 | var arrowRoundCenterY = offsetY + halfArrowSize.height 200 | // 设限阈值 201 | let less = _contentEdgeInsets.top + _bubbleCornerRadius + halfArrowSize.height 202 | let greater = frame.width - _contentEdgeInsets.bottom - _bubbleCornerRadius - halfArrowSize.height 203 | arrowRoundCenterY = arrowRoundCenterY.axc.limitThan(min: less, 204 | max: greater) 205 | switch horizontal { 206 | case .left: 207 | contentRectInsetEdge = _arrowSize.width.axc.edgeInsetsLeft 208 | arrowRoundCenter = .init(x: _arrowRadius + _contentEdgeInsets.left, y: arrowRoundCenterY) 209 | startAngleRadian = (180 - arrowInteriorAngle).axc.angleToRadian 210 | endAngleRadian = (180 + arrowInteriorAngle).axc.angleToRadian 211 | clockwise = true 212 | startPoint = CGPoint(x: _arrowSize.width + _contentEdgeInsets.left, y: offsetY) 213 | 214 | case .right: 215 | contentRectInsetEdge = _arrowSize.width.axc.edgeInsetsRight 216 | arrowRoundCenter = .init(x: frame.width - _arrowRadius - _contentEdgeInsets.right, y: arrowRoundCenterY) 217 | let arrowRoundRadian = arrowInteriorAngle.axc.angleToRadian 218 | startAngleRadian = arrowRoundRadian 219 | endAngleRadian = -arrowRoundRadian 220 | clockwise = false 221 | startPoint = CGPoint(x: frame.width - _arrowSize.width - _contentEdgeInsets.right, y: offsetY) 222 | } 223 | endPoint = CGPoint(x: startPoint.x, y: offsetY + _arrowSize.height) 224 | // 设置垂直限位 225 | endPoint.y = endPoint.y.axc.limitThan(min: arrowOffsetEdgeLimit.top + _arrowSize.height, 226 | max: frame.height - arrowOffsetEdgeLimit.bottom) 227 | startPoint.y = startPoint.y.axc.limitThan(min: arrowOffsetEdgeLimit.top, 228 | max: frame.width - arrowOffsetEdgeLimit.bottom - _arrowSize.height) 229 | } 230 | 231 | // 内容框 232 | let contentRect: CGRect = bounds.axc.inside(edge: _contentEdgeInsets) 233 | let roundedRect: CGRect = contentRect.axc.inside(edge: contentRectInsetEdge) 234 | let maskBezier: AxcBedrockBezierPath = .Axc.Create(roundedRect: roundedRect, 235 | byRoundingCorners: _bubbleCorners, 236 | cornerRadii: _bubbleCornerRadius.axc.cgSize) 237 | // 从圆开始,顺时针 238 | let arrowRoundBezier: AxcBedrockBezierPath = .Axc.Create(arcCenter: arrowRoundCenter, 239 | radius: _arrowRadius, 240 | startAngle: startAngleRadian, 241 | endAngle: endAngleRadian, 242 | clockwise: clockwise) // 逆时针 243 | // 上方起始点 244 | arrowRoundBezier.axc.addLine(to: startPoint) 245 | // 底部结束点 246 | arrowRoundBezier.axc.addLine(to: endPoint) 247 | arrowRoundBezier.close() // 闭合图案 248 | // 两个贝塞尔合并 249 | maskBezier.append(arrowRoundBezier) 250 | _shapeLayer.path = maskBezier.axc.cgPath // 赋值图案 251 | currentLayer?.mask = _shapeLayer 252 | } 253 | 254 | /// 内容间距 255 | var _contentEdgeInsets: AxcBedrockEdgeInsets = .Axc.Create(all: 0) 256 | 257 | /// 箭头位置 258 | var _arrowDirection: ArrowDirection = .rightCenter 259 | /// 箭头大小 260 | var _arrowSize: CGSize = .init(width: 6, height: 12) 261 | /// 箭头圆角半径 262 | var _arrowRadius: CGFloat = 2 263 | 264 | /// 气泡内容间距 265 | var _bubbleContentEdgeInsets: AxcBedrockEdgeInsets = 4.axc.edgeInsets 266 | /// 气泡圆角组 267 | var _bubbleCorners: AxcCorner = .all 268 | /// 气泡圆角半径 269 | var _bubbleCornerRadius: CGFloat = 4 270 | 271 | lazy var _shapeLayer: CAShapeLayer = { 272 | let shapeLayer = CAShapeLayer() 273 | return shapeLayer 274 | }() 275 | 276 | lazy var _contentView: AxcGradientView = { 277 | let view = AxcGradientView() 278 | return view 279 | }() 280 | } 281 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/GradientView/AxcGradientView+Api.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcGradientView+Api.swift 3 | // AxcKit 4 | // 5 | // Created by 赵新 on 2022/12/26. 6 | // 7 | 8 | // MARK: - [AxcGradientViewApi] 9 | 10 | public protocol AxcGradientViewApi: AxcGradientLayerApi { } 11 | 12 | public extension AxcGradientLayerApi where Self: AxcGradientView { 13 | /// 设置渐变样式 14 | /// - Parameter style: 渐变样式 15 | func set(gradientStyle: AxcGradientLayer.Style) { 16 | _set(gradientStyle: gradientStyle) 17 | } 18 | 19 | /// 设置梯度颜色组 20 | /// - Parameter gradientColors: 梯度颜色组 21 | func set(gradientColors: [AxcGradientLayer.GradientColor]?) { 22 | _set(gradientColors: gradientColors) 23 | } 24 | } 25 | 26 | /* 27 | 虽然继承自LayerApi接口协议,但是并没有将方法直接声明到Layer协议中 28 | 主要原因有下: 29 | 1、统一所有类的规范写法 30 | 2、协议中直接声明方法不能带有参数默认值 31 | 3、通过针对性扩展来实现参数默认值 32 | 4、方法参数读写分离,读接口完全复用,写接口框架开发者可以自行定制 33 | */ 34 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/GradientView/AxcGradientView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcKitGradientView.swift 3 | // YPWatermarkCamera 4 | // 5 | // Created by 赵新 on 2022/6/22. 6 | // 7 | 8 | // MARK: - AxcGradientView + AxcGradientViewApi 9 | 10 | extension AxcGradientView: AxcGradientViewApi { } 11 | public extension AxcGradientView { 12 | var gradientStyle: AxcGradientLayer.Style { 13 | return _gradientLayer.gradientStyle 14 | } 15 | 16 | var gradientColors: [AxcGradientLayer.GradientColor]? { 17 | return _gradientLayer.gradientColors 18 | } 19 | } 20 | 21 | extension AxcGradientView { 22 | func _set(gradientStyle: AxcGradientLayer.Style) { 23 | _gradientLayer.set(gradientStyle: gradientStyle) 24 | } 25 | 26 | func _set(gradientColors: [AxcGradientLayer.GradientColor]?) { 27 | _gradientLayer.set(gradientColors: gradientColors) 28 | } 29 | } 30 | 31 | // MARK: - [AxcGradientView] 32 | 33 | /// 渐变视图 34 | open class AxcGradientView: AxcView { 35 | open override class var Axc_layerClass: CALayer.Type { 36 | return AxcGradientLayer.self 37 | } 38 | 39 | var _gradientLayer: AxcGradientLayer { 40 | return layer as! AxcGradientLayer 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/Label/AxcLabel+Api.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcLabel+Api.swift 3 | // AxcKit 4 | // 5 | // Created by 赵新 on 2022/11/24. 6 | // 7 | 8 | import AxcBedrock 9 | 10 | // MARK: - 扩展AxcLabel + AxcCallbackTarget 11 | 12 | public extension AxcLabel { } 13 | 14 | public extension AxcUICallback where Base: AxcLabel { } 15 | 16 | // MARK: - [AxcLabel.NumberOfLineType] 17 | 18 | public extension AxcLabel { 19 | /// 行数类型 20 | enum NumberOfLineType { 21 | /// 单行 22 | case single 23 | /// 多行 24 | case mutable(lineCount: Int) 25 | /// 无限行数 26 | case infinite 27 | } 28 | } 29 | 30 | public extension AxcLabel.MarkLineConfig { 31 | /// 边缘位置 32 | enum EdgePosition { 33 | /// 上 34 | case top(axis: Axis2D) 35 | /// 左 36 | case left(axis: Axis2D) 37 | /// 下 38 | case bottom(axis: Axis2D) 39 | /// 右 40 | case right(axis: Axis2D) 41 | } 42 | 43 | /// 轴向2D 44 | enum Axis2D { 45 | /// 水平 46 | case horizontal(position: AxcPositionVertical) 47 | /// 垂直 48 | case vertical(position: AxcPositionHorizontal) 49 | } 50 | 51 | /// 线样式 52 | enum Style: Equatable { 53 | /// 无 54 | case none 55 | /// 实心 56 | case solid 57 | /// 虚线 58 | case dotted(phase: CGFloat, lengths: [CGFloat]) 59 | } 60 | } 61 | 62 | // MARK: - [AxcLabel.MarkLineConfig] 63 | 64 | public extension AxcLabel { 65 | /// 标线配置对象 66 | struct MarkLineConfig { 67 | /// 线点位 68 | public var edgePosition: EdgePosition 69 | 70 | /// 线颜色 71 | public var color: AxcBedrockColor 72 | 73 | /// 线宽 74 | public var width: CGFloat = 1 75 | 76 | /// 线样式 77 | public var style: Style = .solid 78 | 79 | /// 线末尾样式 80 | public var cap: CGLineCap = .butt 81 | 82 | /// 唯一标识符 83 | public var identifier: String? 84 | 85 | public init(edgePosition: EdgePosition, 86 | color: AxcBedrockColor, 87 | width: CGFloat = 1, 88 | style: Style = .solid, 89 | cap: CGLineCap = .round, 90 | identifier: String? = nil) { 91 | self.edgePosition = edgePosition 92 | self.color = color 93 | self.width = width 94 | self.style = style 95 | self.cap = cap 96 | self.identifier = identifier 97 | } 98 | } 99 | } 100 | 101 | // MARK: - [AxcLabelApi] 102 | 103 | public protocol AxcLabelApi { 104 | // MARK: 内容 105 | 106 | /// 内容边距 107 | var contentEdgeInsets: AxcBedrockEdgeInsets { get } 108 | 109 | // MARK: 对齐 110 | 111 | /// 文字水平对齐 112 | var textPositionHorizontal: AxcPositionHorizontal { get } 113 | 114 | /// 文字垂直对齐 115 | var textPositionVertical: AxcPositionVertical { get } 116 | 117 | /// 文字换行对齐模式 118 | var textPositionHorizontalNewLine: AxcPositionVertical { get } 119 | 120 | // MARK: 文字边框 121 | 122 | /// 文字边框线样式 123 | var textBorderStyle: AxcLabel.MarkLineConfig.Style { get } 124 | 125 | /// 文字边框颜色 126 | var textBorderColor: AxcBedrockColor? { get } 127 | 128 | /// 文字边框宽度 129 | var textBorderWidth: CGFloat { get } 130 | 131 | /// 文字边框线转角类型 132 | var textBorderLineJoin: CGLineJoin { get } 133 | 134 | // MARK: 标线 135 | 136 | /// 标线组 137 | var markLines: [AxcLabel.MarkLineConfig]? { get } 138 | 139 | /// 标线距离文本的边距 140 | var markLineTextEdgeSpacing: AxcBedrockEdgeInsets { get } 141 | } 142 | 143 | // MARK: - 属性设置 144 | 145 | public extension AxcLabelApi where Self: AxcLabel { 146 | // MARK: 内容 147 | 148 | /// 设置内容边距 149 | /// - Parameter contentEdgeInsets: 内容边距 150 | func set(contentEdgeInsets: AxcBedrockEdgeInsets) { 151 | _set(contentEdgeInsets: contentEdgeInsets) 152 | } 153 | 154 | /// 设置文字 155 | /// - Parameter text: 文字 156 | func set(text: AxcUnifiedString) { 157 | _set(text: text) 158 | } 159 | 160 | /// 设置字号 161 | /// - Parameter textFont: 字号 162 | func set(textFont: AxcUnifiedFont) { 163 | _set(textFont: textFont) 164 | } 165 | 166 | /// 设置字色 167 | /// - Parameter textColor: 字色 168 | func set(textColor: AxcUnifiedColor) { 169 | _set(textColor: textColor) 170 | } 171 | 172 | /// 设置文字背景色 173 | /// - Parameter textBackgroundColor: 文字背景色 174 | func set(textBackgroundColor: AxcUnifiedColor) { 175 | _set(textBackgroundColor: textBackgroundColor) 176 | } 177 | 178 | /// 设置文字对齐模式 179 | /// - Parameter textAlignment: 文字对齐模式 180 | func set(textAlignment: NSTextAlignment) { 181 | _set(textAlignment: textAlignment) 182 | } 183 | 184 | /// 设置换行类型 185 | /// - Parameter numberOfLineType: 换行类型 186 | func set(numberOfLineType: NumberOfLineType) { 187 | _set(numberOfLineType: numberOfLineType) 188 | } 189 | 190 | /// 设置文字截断模式 191 | /// - Parameter textLineBreakMode: 文字截断模式 192 | func set(textLineBreakMode: NSLineBreakMode) { 193 | _set(textLineBreakMode: textLineBreakMode) 194 | } 195 | 196 | /// 设置行间距 197 | /// - Parameter lineSpacing: 行间距 198 | func set(lineSpacing: AxcUnifiedNumber) { 199 | _set(lineSpacing: lineSpacing) 200 | } 201 | 202 | // MARK: 对齐 203 | 204 | /// 设置文字水平轴向对齐方式 205 | /// - Parameter textPositionVertical: 文字水平轴向对齐方式 206 | func set(textPositionHorizontal: AxcPositionHorizontal) { 207 | _set(textPositionHorizontal: textPositionHorizontal) 208 | } 209 | 210 | /// 设置文字垂直轴向对齐方式 211 | /// - Parameter textPositionVertical: 文字垂直轴向对齐方式 212 | func set(textPositionVertical: AxcPositionVertical) { 213 | _set(textPositionVertical: textPositionVertical) 214 | } 215 | 216 | // MARK: 文字边框 217 | 218 | /// 设置文字边框的样式 219 | /// - Parameter textBorderStyle: 文字边框的样式 220 | func set(textBorderStyle: MarkLineConfig.Style) { 221 | _set(textBorderStyle: textBorderStyle) 222 | } 223 | 224 | /// 设置文字边框的颜色 225 | /// - Parameter textBorderColor: 文字边框的颜色 226 | func set(textBorderColor: AxcUnifiedColor?) { 227 | _set(textBorderColor: textBorderColor) 228 | } 229 | 230 | /// 设置文字边框的宽度 231 | /// - Parameter textBorderWidth: 文字边框的宽度 232 | func set(textBorderWidth: CGFloat) { 233 | _set(textBorderWidth: textBorderWidth) 234 | } 235 | 236 | /// 设置文字边框的转角类型 237 | /// - Parameter textBorderLineJoin: 文字边框的转角类型 238 | func set(textBorderLineJoin: CGLineJoin) { 239 | _set(textBorderLineJoin: textBorderLineJoin) 240 | } 241 | 242 | // MARK: 标线 243 | 244 | /// 设置标线 245 | /// - Parameter markLines: 标线集合,nil即无标线 246 | func set(markLines: [AxcLabel.MarkLineConfig]?) { 247 | _set(markLines: markLines) 248 | } 249 | 250 | /// 设置标线距离文字的间距 251 | /// - Parameter markLineTextEdgeSpacing: 标线距离文字的间距 252 | func set(markLineTextEdgeSpacing: AxcBedrockEdgeInsets) { 253 | _set(markLineTextEdgeSpacing: markLineTextEdgeSpacing) 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/Label/AxcLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcLabel.swift 3 | // AxcBedrock 4 | // 5 | // Created by 赵新 on 2023/6/30. 6 | // 7 | 8 | import AxcBedrock 9 | 10 | // MARK: - AxcLabel + AxcLabelApi 11 | 12 | extension AxcLabel: AxcLabelApi { 13 | public var contentEdgeInsets: AxcBedrockEdgeInsets { 14 | return _contentEdgeInsets 15 | } 16 | 17 | public var textBorderWidth: CGFloat { 18 | return _textBorderWidth 19 | } 20 | 21 | public var textPositionHorizontal: AxcPositionHorizontal { 22 | return _textPositionHorizontal 23 | } 24 | 25 | public var textPositionVertical: AxcPositionVertical { 26 | return _textPositionVertical 27 | } 28 | 29 | public var textPositionHorizontalNewLine: AxcPositionVertical { 30 | return _textPositionVertical 31 | } 32 | 33 | public var textBorderColor: AxcBedrockColor? { 34 | return AxcBedrockColor.Axc.CreateOptional(_textBorderColor) 35 | } 36 | 37 | public var textBorderStyle: MarkLineConfig.Style { 38 | return _textBorderStyle 39 | } 40 | 41 | public var textBorderLineJoin: CGLineJoin { 42 | return _textBorderLineJoin 43 | } 44 | 45 | public var markLines: [MarkLineConfig]? { 46 | return _markLines 47 | } 48 | 49 | public var markLineTextEdgeSpacing: AxcBedrockEdgeInsets { 50 | return _markLineTextEdgeSpacing 51 | } 52 | } 53 | 54 | extension AxcLabel { 55 | func _set(contentEdgeInsets: AxcBedrockEdgeInsets) { 56 | _contentEdgeInsets = contentEdgeInsets 57 | _updateLayout() 58 | } 59 | 60 | func _set(text: AxcUnifiedString) { 61 | _text = NSAttributedString.Axc.Create(text) // 统一使用富文本 62 | _label.attributedText = _text 63 | _setTextAttributed() 64 | } 65 | 66 | func _set(textFont: AxcUnifiedFont) { 67 | _textFont = textFont 68 | _makeTextAttributed { $0.set(font: textFont) } 69 | } 70 | 71 | func _set(textColor: AxcUnifiedColor) { 72 | _textColor = textColor 73 | _makeTextAttributed { $0.set(foregroundColor: textColor) } 74 | } 75 | 76 | func _set(textBackgroundColor: AxcUnifiedColor) { 77 | _textBackgroundColor = textBackgroundColor 78 | _label.backgroundColor = AxcBedrockColor.Axc.Create(textBackgroundColor) 79 | } 80 | 81 | func _set(textAlignment: NSTextAlignment) { 82 | _textAlignment = textAlignment 83 | _makeParagraphStyle { $0.set(alignment: textAlignment) } 84 | } 85 | 86 | func _set(numberOfLineType: NumberOfLineType) { 87 | _numberOfLineType = numberOfLineType 88 | switch numberOfLineType { 89 | case .single: _label.numberOfLines = 1 90 | case let .mutable(lineCount: lineCount): _label.numberOfLines = lineCount 91 | case .infinite: _label.numberOfLines = 0 92 | } 93 | } 94 | 95 | func _set(textLineBreakMode: NSLineBreakMode) { 96 | _textLineBreakMode = textLineBreakMode 97 | _label.lineBreakMode = textLineBreakMode 98 | } 99 | 100 | func _set(lineSpacing: AxcUnifiedNumber) { 101 | _lineSpacing = lineSpacing 102 | _makeParagraphStyle { $0.set(lineSpacing: lineSpacing) } 103 | } 104 | 105 | func _set(textPositionHorizontal: AxcPositionHorizontal) { 106 | _textPositionHorizontal = textPositionHorizontal 107 | _updateLayout() 108 | } 109 | 110 | func _set(textPositionVertical: AxcPositionVertical) { 111 | _textPositionVertical = textPositionVertical 112 | _updateLayout() 113 | } 114 | 115 | func _set(textBorderStyle: MarkLineConfig.Style) { 116 | _textBorderStyle = textBorderStyle 117 | } 118 | 119 | func _set(textBorderColor: AxcUnifiedColor?) { 120 | _textBorderColor = textBorderColor 121 | } 122 | 123 | func _set(textBorderWidth: CGFloat) { 124 | _textBorderWidth = textBorderWidth 125 | } 126 | 127 | func _set(textBorderLineJoin: CGLineJoin) { 128 | _textBorderLineJoin = textBorderLineJoin 129 | } 130 | 131 | func _set(markLines: [MarkLineConfig]?) { 132 | _markLines = markLines 133 | } 134 | 135 | func _set(markLineTextEdgeSpacing: AxcBedrockEdgeInsets) { 136 | _markLineTextEdgeSpacing = markLineTextEdgeSpacing 137 | _updateLayout() 138 | } 139 | } 140 | 141 | // MARK: - [AxcLabel] 142 | 143 | open class AxcLabel: AxcGradientView { 144 | open override func makeUI() { 145 | super.makeUI() 146 | // 内容视图 147 | addSubview(contentView) 148 | // 文本标签视图 149 | contentView.addSubview(_label) 150 | _setTextAttributed() 151 | _updateLayout() 152 | } 153 | 154 | func _updateLayout() { 155 | guard _label.superview != nil else { return } 156 | contentView.snp.remakeConstraints { make in 157 | make.edges.equalToSuperview().inset(_contentEdgeInsets) 158 | } 159 | _label.snp.remakeConstraints { make in 160 | make.left.greaterThanOrEqualToSuperview().offset(_markLineTextEdgeSpacing.left) 161 | make.right.lessThanOrEqualToSuperview().offset(-_markLineTextEdgeSpacing.right) 162 | make.top.greaterThanOrEqualToSuperview().offset(_markLineTextEdgeSpacing.top) 163 | make.bottom.lessThanOrEqualToSuperview().offset(-_markLineTextEdgeSpacing.bottom) 164 | // 文字水平对齐 165 | switch _textPositionHorizontal { 166 | case .left: 167 | make.left.equalToSuperview().offset(_markLineTextEdgeSpacing.left) 168 | case .center: 169 | make.centerX.equalToSuperview() 170 | case .right: 171 | make.right.equalToSuperview().offset(-_markLineTextEdgeSpacing.right) 172 | } 173 | // 文字垂直对齐 174 | switch _textPositionVertical { 175 | case .top: 176 | make.top.equalToSuperview().offset(_markLineTextEdgeSpacing.top) 177 | case .center: 178 | make.centerY.equalToSuperview() 179 | case .bottom: 180 | make.bottom.equalToSuperview().offset(-_markLineTextEdgeSpacing.bottom) 181 | } 182 | } 183 | } 184 | 185 | /// 设置默认值/存储值 186 | func _setTextAttributed() { 187 | // label属性 188 | _set(textBackgroundColor: _textBackgroundColor) 189 | _set(numberOfLineType: _numberOfLineType) 190 | _set(textLineBreakMode: _textLineBreakMode) 191 | // 富文本属性 192 | _paragraphStyle = _paragraphStyle.axc.makeParagraphStyle { 193 | $0.set(alignment: _textAlignment) 194 | .set(lineSpacing: _lineSpacing) 195 | } 196 | _makeTextAttributed { 197 | $0.set(font: _textFont) 198 | .set(foregroundColor: _textColor) 199 | .set(paragraphStyle: _paragraphStyle) 200 | } 201 | } 202 | 203 | /// 设置富文本属性 204 | func _makeTextAttributed(_ makeBlock: AxcBlock.Maker) { 205 | _text = _label.attributedText?.axc.makeAttributed(makeBlock) 206 | _label.attributedText = nil // 需要触发重绘 207 | _label.attributedText = _text 208 | } 209 | 210 | /// 设置段落 211 | func _makeParagraphStyle(_ makeBlock: AxcBlock.Maker) { 212 | _paragraphStyle = _paragraphStyle.axc.makeParagraphStyle(makeBlock) 213 | _makeTextAttributed { make in 214 | make.set(paragraphStyle: _paragraphStyle) 215 | } 216 | } 217 | 218 | /// ----内容---- 219 | /// 内容边距 220 | var _contentEdgeInsets: AxcBedrockEdgeInsets = .Axc.Create(0) 221 | /// 文字 222 | var _text: NSAttributedString? 223 | /// 文字大小 224 | var _textFont: AxcUnifiedFont = 18 225 | /// 文字颜色 226 | var _textColor: AxcUnifiedColor = "000000" 227 | /// 文字背景色 228 | var _textBackgroundColor: AxcUnifiedColor = AxcBedrockColor.clear 229 | /// 文字对齐模式 230 | var _textAlignment: NSTextAlignment = .left 231 | /// 文字换行类型 232 | var _numberOfLineType: NumberOfLineType = .infinite 233 | /// 文字截断模式 234 | var _textLineBreakMode: NSLineBreakMode = .byTruncatingTail 235 | /// 文字行间距 236 | var _lineSpacing: AxcUnifiedNumber = 2 237 | 238 | /// ----文字---- 239 | /// 文字水平对齐模式 240 | var _textPositionHorizontal: AxcPositionHorizontal = .center 241 | /// 文字垂直对齐模式 242 | var _textPositionVertical: AxcPositionVertical = .center 243 | 244 | /// ----边框---- 245 | /// 文字边框线样式 246 | var _textBorderStyle: MarkLineConfig.Style = .none 247 | /// 文字边框宽度 248 | var _textBorderWidth: CGFloat = 1 249 | /// 文字边框宽度 250 | var _textBorderColor: AxcUnifiedColor? 251 | /// 文字边框线转角类型 252 | var _textBorderLineJoin: CGLineJoin = .round 253 | 254 | /// ----标线---- 255 | /// 标线配置组 256 | var _markLines: [MarkLineConfig]? 257 | /// 标线距离文本的边距 258 | var _markLineTextEdgeSpacing: AxcBedrockEdgeInsets = .Axc.Create(0) 259 | 260 | /// 文字段落样式 261 | lazy var _paragraphStyle: NSMutableParagraphStyle = { 262 | let paragraphStyle = NSMutableParagraphStyle() 263 | return paragraphStyle 264 | }() 265 | 266 | lazy var contentView: AxcView = { 267 | let view = AxcView() 268 | view.set(backgroundColor: AxcBedrockColor.clear) 269 | return view 270 | }() 271 | 272 | // ----获取---- 273 | #if os(macOS) 274 | open lazy var _label: _AxcNSLabel = { 275 | let label = _AxcNSLabel() 276 | return label 277 | }() 278 | 279 | #elseif os(iOS) || os(tvOS) || os(watchOS) 280 | 281 | lazy var _label: _AxcUILabel = { 282 | let label = _AxcUILabel() 283 | return label 284 | }() 285 | #endif 286 | } 287 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/Label/Bridge/_AxcNSLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // _AxcNSLabel.swift 3 | // Pods 4 | // 5 | // Created by 赵新 on 2023/7/2. 6 | // 7 | 8 | #if canImport(AppKit) 9 | import AppKit 10 | 11 | open class _AxcNSLabel: NSTextField { 12 | public convenience init() { 13 | self.init(frame: .zero) 14 | } 15 | 16 | public override init(frame frameRect: CGRect) { 17 | super.init(frame: frameRect) 18 | defaultConfig() 19 | } 20 | 21 | @available(*, unavailable) 22 | public required init?(coder: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | 26 | open override func hitTest(_ point: NSPoint) -> NSView? { 27 | isEnabled ? super.hitTest(point) : nil 28 | } 29 | 30 | func defaultConfig() { 31 | isBezeled = false 32 | isEditable = false // 不可编辑 33 | isBordered = false // 不显示边框 34 | isSelectable = false 35 | drawsBackground = true // 渲染背景色 36 | } 37 | 38 | // MARK: 桥接属性(这里没采用Api协议方式是因为需要统一Label默认的Api) 39 | 40 | /// 文字对齐 41 | var textAlignment: NSTextAlignment { 42 | set { alignment = newValue } 43 | get { return alignment } 44 | } 45 | 46 | /// 富文本 47 | var attributedText: NSAttributedString? { 48 | set { attributedStringValue = newValue ?? NSAttributedString() } 49 | get { return attributedStringValue } 50 | } 51 | 52 | /// 行数 53 | var numberOfLines: Int { 54 | set { maximumNumberOfLines = newValue } 55 | get { return maximumNumberOfLines } 56 | } 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/Label/Bridge/_AxcUILabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // _AxcUILabel.swift 3 | // Pods 4 | // 5 | // Created by 赵新 on 2023/7/2. 6 | // 7 | 8 | #if canImport(UIKit) 9 | import UIKit 10 | 11 | open class _AxcUILabel: UILabel { } 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/TableView/AxcTableView+Api.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcTableView+Api.swift 3 | // Alamofire 4 | // 5 | // Created by 赵新 on 26/9/2023. 6 | // 7 | 8 | import AxcBedrock 9 | 10 | // MARK: - [AxcTableViewApi] 11 | 12 | public protocol AxcTableViewApi { } 13 | 14 | extension AxcTableViewApi where Self: AxcTableView { } 15 | 16 | // MARK: - AxcTargetDragView + AxcUICallbackTarget 17 | 18 | public extension AxcUICallback where Base: AxcTableView { } 19 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/TableView/AxcTableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcTableView.swift 3 | // Alamofire 4 | // 5 | // Created by 赵新 on 26/9/2023. 6 | // 7 | 8 | import AxcBedrock 9 | import AxcUIKit 10 | 11 | open class AxcTableView: AxcView { } 12 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/TableView/Bridge/_AxcNSTableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // _AxcNSTableView.swift 3 | // Alamofire 4 | // 5 | // Created by 赵新 on 26/9/2023. 6 | // 7 | 8 | #if canImport(AppKit) 9 | import AxcBedrock 10 | 11 | public protocol AxcNSTableViewDelegate { 12 | func tableView(tableView: _AxcNSTableView, heightForHeaderInSection section: Int) -> CGFloat 13 | func tableView(tableView: _AxcNSTableView, viewForHeaderInSection section: Int) -> NSView? 14 | func tableView(tableView: _AxcNSTableView, shouldSelectHeaderInSection section: Int) -> Bool 15 | 16 | func tableView(tableView: _AxcNSTableView, heightForRowAt indexPath: IndexPath) -> CGFloat 17 | func tableView(tableView: _AxcNSTableView, shouldSelectCellAtIndexPath indexPath: IndexPath) -> Bool 18 | 19 | func tableView(tableView: _AxcNSTableView, heightForFooterInSection section: Int) -> CGFloat 20 | func tableView(tableView: _AxcNSTableView, viewForFooterInSection section: Int) -> NSView? 21 | func tableView(tableView: _AxcNSTableView, shouldSelectFooterInSection section: Int) -> Bool 22 | } 23 | 24 | public extension AxcNSTableViewDelegate { 25 | func tableView(tableView: _AxcNSTableView, heightForHeaderInSection section: Int) -> CGFloat { return .Axc.Min } 26 | func tableView(tableView: _AxcNSTableView, viewForHeaderInSection section: Int) -> NSView? { return nil } 27 | func tableView(tableView: _AxcNSTableView, shouldSelectHeaderInSection section: Int) -> Bool { return false } 28 | 29 | func tableView(tableView: _AxcNSTableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 44 } 30 | func tableView(tableView: _AxcNSTableView, shouldSelectCellAtIndexPath indexPath: IndexPath) -> Bool { return true } 31 | 32 | func tableView(tableView: _AxcNSTableView, heightForFooterInSection section: Int) -> CGFloat { return .Axc.Min } 33 | func tableView(tableView: _AxcNSTableView, viewForFooterInSection section: Int) -> NSView? { return nil } 34 | func tableView(tableView: _AxcNSTableView, shouldSelectFooterInSection section: Int) -> Bool { return false } 35 | } 36 | 37 | public protocol AxcNSTableViewDataSource { 38 | /// 组数量 39 | func numberOfSectionsInTableView(tableView: _AxcNSTableView) -> Int 40 | /// 每组行数量 41 | func tableView(tableView: _AxcNSTableView, numberOfRowsInSection section: Int) -> Int 42 | /// 获取Cell 43 | func tableView(tableView: _AxcNSTableView, cellForRowAt indexPath: IndexPath) -> AxcTableViewCell 44 | } 45 | 46 | public extension AxcNSTableViewDataSource { 47 | func numberOfSectionsInTableView(tableView: _AxcNSTableView) -> Int { return 1 } 48 | } 49 | 50 | public extension _AxcNSTableView { 51 | func reloadData() { 52 | _tableView.reloadData() 53 | } 54 | } 55 | 56 | public extension _AxcNSTableView { 57 | /// 每行元素的类型 58 | enum RowItemType { 59 | case tableHeaderView(view: NSView) 60 | case sectionHeaderView(section: Int) 61 | case cell(indexPath: IndexPath) 62 | case sectionFooterView(section: Int) 63 | case tableFooterView(view: NSView) 64 | } 65 | } 66 | 67 | open class _AxcNSTableView: AxcView { 68 | open override func axc_layoutSubviews() { 69 | super.axc_layoutSubviews() 70 | _tableView.frame = bounds 71 | } 72 | 73 | open override func makeUI() { 74 | super.makeUI() 75 | 76 | addSubview(_tableView) 77 | } 78 | 79 | open var delegate: AxcNSTableViewDelegate? 80 | open var dataSource: AxcNSTableViewDataSource? 81 | 82 | open var tableHeaderView: NSView? 83 | open var tableFooterView: NSView? 84 | 85 | /// 组表 86 | private var _rowItemTypeList: [RowItemType] = [] 87 | 88 | private lazy var _tableView: NSTableView = { 89 | let tableView = NSTableView() 90 | tableView.delegate = self 91 | tableView.dataSource = self 92 | return tableView 93 | }() 94 | } 95 | 96 | extension _AxcNSTableView { 97 | /// 构建二维组数组 98 | func _constructSectionModelList() -> [RowItemType] { 99 | var rowItemTypeList: [RowItemType] = [] 100 | // All cells 101 | guard let dataSource, 102 | let delegate 103 | else { return [] } 104 | if let tableHeaderView { // 头视图 105 | rowItemTypeList.append(.tableHeaderView(view: tableHeaderView)) 106 | } 107 | for section in 0 ..< dataSource.numberOfSectionsInTableView(tableView: self) { 108 | // The headers count in section 109 | // 这组的组头视图数量 110 | if let _ = delegate.tableView(tableView: self, viewForHeaderInSection: section) { 111 | rowItemTypeList.append(.sectionHeaderView(section: section)) 112 | } 113 | // The cell count in section 114 | // 这组的cell数量 115 | let cellCount = dataSource.tableView(tableView: self, numberOfRowsInSection: section) 116 | for row in 0 ..< cellCount { 117 | let indexPath = IndexPath(item: row, section: section) 118 | rowItemTypeList.append(.cell(indexPath: indexPath)) 119 | } 120 | // The footers count in section 121 | // 这组的组尾视图数量 122 | if let _ = delegate.tableView(tableView: self, viewForFooterInSection: section) { 123 | rowItemTypeList.append(.sectionFooterView(section: section)) 124 | } 125 | } 126 | if let tableFooterView { // 头视图 127 | rowItemTypeList.append(.tableFooterView(view: tableFooterView)) 128 | } 129 | /* 130 | Because this table is a single group 131 | it is necessary to make multiple group effects in a single group 132 | 因为这个table是单组的,所以要在单组中做出多组的效果 133 | */ 134 | return rowItemTypeList 135 | } 136 | } 137 | 138 | extension _AxcNSTableView: NSTableViewDelegate { 139 | public func tableView(_ tableView: NSTableView, setObjectValue object: Any?, for tableColumn: NSTableColumn?, row: Int) { } 140 | 141 | public func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? { 142 | guard let rowItemType = _rowItemTypeList.axc.object(at: row) else { return nil } 143 | return rowItemType 144 | } 145 | 146 | // public func tableView(_ tableView: NSTableView, dataCellFor tableColumn: NSTableColumn?, row: Int) -> NSCell? { 147 | // if let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CellView"), 148 | // owner: self) { 149 | // return cellView 150 | // } else { 151 | // guard let dataSource = dataSource, 152 | // let rowItemType = _rowItemTypeList.axc.object(at: row) 153 | // else { return nil } 154 | // var cellView: NSView? 155 | // switch rowItemType { 156 | // case let .tableHeaderView(view: view): 157 | // cellView = view 158 | // case let .sectionHeaderView(section: section): 159 | // if let headerView = delegate?.tableView(tableView: self, viewForHeaderInSection: section) { 160 | // cellView = headerView 161 | // } 162 | // case let .cell(indexPath: indexPath): 163 | // let cell = dataSource.tableView(tableView: self, cellForRowAt: indexPath) 164 | // cellView = cell 165 | // case let .sectionFooterView(section: section): 166 | // if let footerView = delegate?.tableView(tableView: self, viewForFooterInSection: section) { 167 | // cellView = footerView 168 | // } 169 | // case let .tableFooterView(view: view): 170 | // cellView = view 171 | // } 172 | // return cellView 173 | // } 174 | // } 175 | 176 | public func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool { 177 | guard let delegate = delegate, 178 | let rowItemType = _rowItemTypeList.axc.object(at: row) 179 | else { return false } 180 | // 获取索引 181 | switch rowItemType { 182 | case .tableHeaderView: 183 | return false 184 | case let .sectionHeaderView(section: section): 185 | return delegate.tableView(tableView: self, shouldSelectHeaderInSection: section) 186 | case let .cell(indexPath: indexPath): 187 | return delegate.tableView(tableView: self, shouldSelectCellAtIndexPath: indexPath) 188 | case let .sectionFooterView(section: section): 189 | return delegate.tableView(tableView: self, shouldSelectFooterInSection: section) 190 | case .tableFooterView: 191 | return false 192 | } 193 | } 194 | 195 | public func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { 196 | guard let delegate = delegate, 197 | let rowItemType = _rowItemTypeList.axc.object(at: row) 198 | else { return .Axc.Min } 199 | // 获取索引 200 | switch rowItemType { 201 | case let .tableHeaderView(view: view): 202 | return view.frame.height 203 | case let .sectionHeaderView(section: section): 204 | return delegate.tableView(tableView: self, heightForHeaderInSection: section) 205 | case let .cell(indexPath: indexPath): 206 | return delegate.tableView(tableView: self, heightForRowAt: indexPath) 207 | case let .sectionFooterView(section: section): 208 | return delegate.tableView(tableView: self, heightForFooterInSection: section) 209 | case let .tableFooterView(view: view): 210 | return view.frame.height 211 | } 212 | } 213 | } 214 | 215 | extension _AxcNSTableView: NSTableViewDataSource { 216 | /// All counts of rows 217 | /// 所有行数 218 | public func numberOfRows(in tableView: NSTableView) -> Int { 219 | _rowItemTypeList = _constructSectionModelList() // 统计 220 | return _rowItemTypeList.count 221 | } 222 | } 223 | 224 | #endif 225 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/TableView/Bridge/_AxcUITableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // _AxcUITableView.swift 3 | // Alamofire 4 | // 5 | // Created by 赵新 on 26/9/2023. 6 | // 7 | 8 | #if canImport(UIKit) 9 | import UIKit 10 | 11 | class _AxcUITableView: AxcView { } 12 | #endif 13 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/CrossPlatform/TableViewCell/AxcTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcTableViewCell.swift 3 | // Alamofire 4 | // 5 | // Created by 赵新 on 26/9/2023. 6 | // 7 | 8 | import Cocoa 9 | 10 | open class AxcTableViewCell: AxcView { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/QuartzCore/GradientLayer/AxcGradientLayer+Api.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcGradientLayer+Api.swift 3 | // AxcKit 4 | // 5 | // Created by 赵新 on 2022/12/24. 6 | // 7 | 8 | import QuartzCore 9 | import AxcBedrock 10 | 11 | // MARK: - [AxcGradientLayer.Style] 12 | 13 | public extension AxcGradientLayer { 14 | /// 样式 15 | enum Style { 16 | /// 轴向 17 | public enum AxisStyle: Int { 18 | /// 从上到下 19 | case topToBottom = 0 20 | /// 从左到右 21 | case leftToRight 22 | /// 从下到上 23 | case bottomToTop 24 | /// 从右到左 25 | case rightToLeft 26 | } 27 | 28 | /// 斜轴 29 | public enum AxisTiltStyle: Int { 30 | /// 从左上到右下 31 | case topLeftToBottomRight = 0 32 | /// 从右上到左下 33 | case topRightToBottomLeft 34 | /// 从左下到右上 35 | case bottomLeftToTopRight 36 | /// 从右下到左上 37 | case bottomRightToTopLeft 38 | } 39 | 40 | /// 轴向 41 | case axis(_ axisStyle: AxisStyle) 42 | /// 斜轴 43 | case axisTilt(_ axisTiltStyle: AxisTiltStyle) 44 | /// 锥形(需要处理收尾相连) 45 | case conic 46 | /// 径向/环形 47 | case radial 48 | } 49 | } 50 | 51 | // MARK: - [AxcGradientLayer.GradientColor] 52 | 53 | public extension AxcGradientLayer { 54 | /// 梯度颜色对象 55 | struct GradientColor { 56 | // Lifecycle 57 | 58 | public init(color: AxcUnifiedColor, 59 | location: CGFloat? = nil) { 60 | self.color = color 61 | self.location = location 62 | } 63 | 64 | // Public 65 | 66 | /// 颜色 67 | public var color: AxcUnifiedColor 68 | 69 | /// 每个颜色的渐变结束的点位,元素数量需要 70 | /// 取值 0 - 1 71 | /// 例如: 72 | /// 73 | /// 梯度颜色设置: [.red, .green] 74 | /// 3-7分点位应取:[0.3, 0.7] 75 | /// 76 | public var location: CGFloat? 77 | } 78 | } 79 | 80 | // MARK: - [AxcGradientLayerApi] 81 | 82 | public protocol AxcGradientLayerApi { 83 | /// 样式 84 | var gradientStyle: AxcGradientLayer.Style { get } 85 | 86 | /// 梯度颜色组 87 | var gradientColors: [AxcGradientLayer.GradientColor]? { get } 88 | } 89 | 90 | public extension AxcGradientLayerApi where Self: AxcGradientLayer { 91 | /// 设置渐变样式 92 | /// - Parameter style: 渐变样式 93 | func set(gradientStyle: AxcGradientLayer.Style) { 94 | _set(gradientStyle: gradientStyle) 95 | } 96 | 97 | /// 设置梯度颜色组 98 | /// - Parameter gradientColors: 梯度颜色组 99 | func set(gradientColors: [AxcGradientLayer.GradientColor]?) { 100 | _set(gradientColors: gradientColors) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/QuartzCore/GradientLayer/AxcGradientLayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcGradientLayer.swift 3 | // AxcKit 4 | // 5 | // Created by 赵新 on 2022/12/24. 6 | // 7 | 8 | import QuartzCore 9 | import AxcBedrock 10 | 11 | extension AxcGradientLayer.Style { 12 | /// 水垂方向 13 | var topPoint: CGPoint { return .init(x: 0.5, y: 0) } 14 | var leftPoint: CGPoint { return .init(x: 0, y: 0.5) } 15 | var bottomPoint: CGPoint { return .init(x: 0.5, y: 1) } 16 | var rightPoint: CGPoint { return .init(x: 1, y: 0.5) } 17 | /// 斜方向 18 | var topLeftPoint: CGPoint { return .init(x: 0, y: 0) } 19 | var topRightPoint: CGPoint { return .init(x: 1, y: 0) } 20 | var bottomLeftPoint: CGPoint { return .init(x: 0, y: 1) } 21 | var bottomRightPoint: CGPoint { return .init(x: 1, y: 1) } 22 | /// 中心 23 | var centerPoint: CGPoint { return .init(x: 0.5, y: 0.5) } 24 | /// 起始点 25 | var startPoint: CGPoint { 26 | switch self { 27 | case let .axis(axisStyle): 28 | var point: CGPoint = .zero 29 | switch axisStyle { 30 | case .topToBottom: point = topPoint 31 | case .leftToRight: point = leftPoint 32 | case .bottomToTop: point = bottomPoint 33 | case .rightToLeft: point = rightPoint 34 | } 35 | return point 36 | case let .axisTilt(axisTiltStyle): 37 | var point: CGPoint = .zero 38 | switch axisTiltStyle { 39 | case .topLeftToBottomRight: point = topLeftPoint 40 | case .topRightToBottomLeft: point = topRightPoint 41 | case .bottomLeftToTopRight: point = bottomLeftPoint 42 | case .bottomRightToTopLeft: point = bottomRightPoint 43 | } 44 | return point 45 | case .conic: 46 | return centerPoint 47 | case .radial: 48 | return centerPoint 49 | } 50 | } 51 | 52 | /// 终止点 53 | var endPoint: CGPoint { 54 | switch self { 55 | case let .axis(axisStyle): 56 | var point: CGPoint = .zero 57 | switch axisStyle { 58 | case .topToBottom: point = bottomPoint 59 | case .leftToRight: point = rightPoint 60 | case .bottomToTop: point = topPoint 61 | case .rightToLeft: point = leftPoint 62 | } 63 | return point 64 | case let .axisTilt(axisTiltStyle): 65 | var point: CGPoint = .zero 66 | switch axisTiltStyle { 67 | case .topLeftToBottomRight: point = bottomRightPoint 68 | case .topRightToBottomLeft: point = bottomLeftPoint 69 | case .bottomLeftToTopRight: point = topRightPoint 70 | case .bottomRightToTopLeft: point = topLeftPoint 71 | } 72 | return point 73 | case .conic: 74 | return topPoint 75 | case .radial: 76 | return bottomRightPoint 77 | } 78 | } 79 | 80 | var type: CAGradientLayerType { 81 | switch self { 82 | case .axis: return .axial 83 | case .axisTilt: return .axial 84 | case .conic: 85 | if #available(iOS 12.0, *) { 86 | return .conic 87 | } else { 88 | AxcUIKitLib.Log("\(self)系统版本低于iOS12,无法使用锥形渐变样式!将自动替换为梯度样式", logLevel: .warning) 89 | return .axial 90 | } 91 | case .radial: return .radial 92 | } 93 | } 94 | } 95 | 96 | // MARK: - AxcGradientLayer + AxcGradientLayerApi 97 | 98 | extension AxcGradientLayer: AxcGradientLayerApi { 99 | public var gradientStyle: AxcGradientLayer.Style { 100 | return _gradientStyle 101 | } 102 | 103 | public var gradientColors: [AxcGradientLayer.GradientColor]? { 104 | return _gradientColors 105 | } 106 | } 107 | 108 | extension AxcGradientLayer { 109 | /// 设置渐变样式 110 | /// - Parameter style: 渐变样式 111 | func _set(gradientStyle: AxcGradientLayer.Style) { 112 | _gradientStyle = gradientStyle 113 | _gradientLayer.type = _gradientStyle.type 114 | _gradientLayer.startPoint = _gradientStyle.startPoint 115 | _gradientLayer.endPoint = _gradientStyle.endPoint 116 | } 117 | 118 | /// 设置梯度颜色组 119 | /// - Parameter gradientColors: 梯度颜色组 120 | func _set(gradientColors: [AxcGradientLayer.GradientColor]?) { 121 | _gradientColors = gradientColors 122 | _setGradientColorsParams(_gradientColors) 123 | } 124 | } 125 | 126 | // MARK: - [AxcGradientLayer] 127 | 128 | open class AxcGradientLayer: AxcLayer { 129 | open override func layoutSublayers() { 130 | super.layoutSublayers() 131 | _gradientLayer.frame = bounds 132 | } 133 | 134 | /// 创建UI的方法放在这里 135 | /// 最好按照,从上至下,从左至右依次添加视图 136 | open override func makeUI() { 137 | super.makeUI() 138 | addSublayer(_gradientLayer) 139 | } 140 | 141 | /// 样式 142 | var _gradientStyle: Style = .axis(.leftToRight) 143 | /// 梯度颜色 144 | var _gradientColors: [GradientColor]? 145 | 146 | lazy var _gradientLayer: CAGradientLayer = { 147 | let gradientLayer = CAGradientLayer() 148 | return gradientLayer 149 | }() 150 | } 151 | 152 | extension AxcGradientLayer { 153 | /// 设置参数到渐变Layer 154 | func _setGradientColorsParams(_ gradientColors: [GradientColor]?) { 155 | if let gradientColors = gradientColors { 156 | var colors: [CGColor] = [] 157 | var locations: [CGFloat] = [] 158 | for item in gradientColors { 159 | if let color = AxcBedrockColor.Axc.CreateOptional(item.color)?.cgColor { 160 | colors.append(color) 161 | } 162 | if let location = item.location { 163 | locations.append(location) 164 | } 165 | } 166 | 167 | _gradientLayer.colors = colors 168 | if locations.isEmpty { 169 | _gradientLayer.locations = nil 170 | } else { 171 | if colors.count != locations.count { 172 | AxcUIKitLib.Log("\(self)渐变色视图的颜色与点位数量不符!", logLevel: .warning) 173 | } else { 174 | _gradientLayer.locations = locations.map { $0.axc.number } 175 | } 176 | } 177 | } else { 178 | _gradientLayer.colors = nil 179 | _gradientLayer.locations = nil 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/QuartzCore/Layer/AxcLayer+Api.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcLayer+Api.swift 3 | // AxcBedrock 4 | // 5 | // Created by 赵新 on 2023/6/22. 6 | // 7 | 8 | // MARK: - [AxcLayerApi] 9 | 10 | public protocol AxcLayerApi { } 11 | public extension AxcLayerApi where Self: AxcLayer { } 12 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/QuartzCore/Layer/AxcLayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcLayer.swift 3 | // AxcBedrock 4 | // 5 | // Created by 赵新 on 2023/6/22. 6 | // 7 | 8 | import AxcBedrock 9 | import QuartzCore 10 | 11 | // MARK: - AxcLayer + AxcUIBasicFuncTarget 12 | 13 | extension AxcLayer: AxcUIBasicFuncTarget { } 14 | 15 | // MARK: - [AxcLayer] 16 | 17 | open class AxcLayer: CALayer { 18 | // Lifecycle 19 | 20 | public override init(layer: Any) { 21 | super.init(layer: layer) 22 | } 23 | 24 | public override init() { 25 | super.init() 26 | performBasic() 27 | performDataChannel() 28 | } 29 | 30 | @available(*, unavailable) 31 | public required init?(coder: NSCoder) { 32 | super.init(coder: coder) 33 | } 34 | 35 | // Open 36 | 37 | /// 配置数据的地方 38 | open func config() { 39 | backgroundColor = AxcBedrockColor.white.cgColor 40 | } 41 | 42 | /// 创建UI的方法放在这里。按照,从上至下,从左至右依次添加视图 43 | open func makeUI() { } 44 | 45 | /// 数据即将刷新 46 | open func dataWillLoad() { } 47 | 48 | /// 正向数据流(页面操作的监听)方法放在这里 49 | open func bindViewAction() { } 50 | 51 | /// 反向数据流(ViewModel的监听)进行数据绑定的放在这里 52 | open func bindViewModel() { } 53 | 54 | /// 通知数据流,主要用于接口暴露和复用 55 | open func bindNotice() { } 56 | 57 | /// 驱动数据流,主要用于外部驱动 58 | open func bindDrive() { } 59 | } 60 | -------------------------------------------------------------------------------- /AxcUIKit/Classes/UIKit/Views/Basic/AxcCollectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AxcCollectionView.swift 3 | // AxcUIKit 4 | // 5 | // Created by 赵新 on 2023/6/22. 6 | // 7 | 8 | #if canImport(UIKit) 9 | 10 | import UIKit 11 | 12 | class AxcCollectionView: UICollectionView { 13 | /* 14 | // Only override draw() if you perform custom drawing. 15 | // An empty implementation adversely affects performance during animation. 16 | override func draw(_ rect: CGRect) { 17 | // Drawing code 18 | } 19 | */ 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /Example/AxcUIKit.xcodeproj/xcshareddata/xcschemes/macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 35 | 41 | 42 | 43 | 44 | 45 | 55 | 57 | 63 | 64 | 65 | 66 | 72 | 74 | 80 | 81 | 82 | 83 | 85 | 86 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | # iOS平台iPhone、iPad 4 | # iOS单元测试 5 | # iOS扩展小组件 6 | ["iOS", "iOSTests", "iOS_WidgetExtension"].each do |t| 7 | target t do 8 | platform :ios, '11.0' 9 | use_frameworks! 10 | pod 'AxcUIKit', :path => '../' 11 | pod 'AxcBedrock', :path => '/Users/zhaoxin/Axc组件/AxcBedrock-Swift' 12 | end 13 | end 14 | 15 | 16 | # macOS平台Mac 17 | # macOS单元测试 18 | ["macOS", "macOSTests"].each do |t| 19 | target t do 20 | platform :osx, '11.0' 21 | use_frameworks! 22 | pod 'AxcUIKit', :path => '../' 23 | pod 'AxcBedrock', :path => '/Users/zhaoxin/Axc组件/AxcBedrock-Swift' 24 | end 25 | end 26 | 27 | -------------------------------------------------------------------------------- /Example/iOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // iOS 4 | // 5 | // Created by 赵新 on 2023/6/30. 6 | // Copyright © 2023 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @main 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Example/iOS/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Example/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Example/iOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/iOS/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Example/iOS/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Example/iOS/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // iOS 4 | // 5 | // Created by 赵新 on 2023/6/30. 6 | // Copyright © 2023 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AxcUIKit 11 | import AxcBedrock 12 | 13 | class ViewController: UIViewController { 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | // Do any additional setup after loading the view. 18 | 19 | view.backgroundColor = .systemGray 20 | 21 | let label = AxcLabel() 22 | label.set(text: "Hello WorldHello WorldHello WorldHello WorldHello WorldHello World") 23 | label.set(textFont: 16.axc.uiFont(weight: .bold)) 24 | label.set(textBackgroundColor: "FF0000") 25 | label.set(contentEdgeInsets: 8.axc.edge) 26 | label.set(numberOfLineType: .infinite) 27 | // label.set(textLineBreakMode: .byTruncatingTail) 28 | view.addSubview(label) 29 | 30 | label.snp.makeConstraints { make in 31 | make.center.equalToSuperview() 32 | make.width.equalTo(120) 33 | // make.height.equalTo(120) 34 | } 35 | 36 | AxcGCD.Delay(delay: 2) { 37 | label.set(lineSpacing: 20) 38 | print("延迟结束") 39 | } 40 | AxcGCD.Delay(delay: 3) { 41 | label.set(textBackgroundColor: UIColor.green) 42 | print("延迟结束") 43 | } 44 | AxcGCD.Delay(delay: 4) { 45 | label.set(text: "Hello World") 46 | print("延迟结束") 47 | } 48 | 49 | } 50 | 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /Example/iOSTests/iOSTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // iOSTests.swift 3 | // iOSTests 4 | // 5 | // Created by 赵新 on 2023/6/30. 6 | // Copyright © 2023 CocoaPods. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import iOS 11 | 12 | final class iOSTests: XCTestCase { 13 | 14 | override func setUpWithError() throws { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDownWithError() throws { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() throws { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | // Any test you write for XCTest can be annotated as throws and async. 26 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. 27 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. 28 | } 29 | 30 | func testPerformanceExample() throws { 31 | // This is an example of a performance test case. 32 | self.measure { 33 | // Put the code you want to measure the time of here. 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Example/iOS_Widget/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Example/iOS_Widget/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Example/iOS_Widget/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/iOS_Widget/Assets.xcassets/WidgetBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Example/iOS_Widget/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSExtension 6 | 7 | NSExtensionPointIdentifier 8 | com.apple.widgetkit-extension 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Example/iOS_Widget/iOS_Widget.intentdefinition: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | INEnums 6 | 7 | INIntentDefinitionModelVersion 8 | 1.2 9 | INIntentDefinitionNamespace 10 | 88xZPY 11 | INIntentDefinitionSystemVersion 12 | 20A294 13 | INIntentDefinitionToolsBuildVersion 14 | 12A6144 15 | INIntentDefinitionToolsVersion 16 | 12.0 17 | INIntents 18 | 19 | 20 | INIntentCategory 21 | information 22 | INIntentDescriptionID 23 | tVvJ9c 24 | INIntentEligibleForWidgets 25 | 26 | INIntentIneligibleForSuggestions 27 | 28 | INIntentName 29 | Configuration 30 | INIntentResponse 31 | 32 | INIntentResponseCodes 33 | 34 | 35 | INIntentResponseCodeName 36 | success 37 | INIntentResponseCodeSuccess 38 | 39 | 40 | 41 | INIntentResponseCodeName 42 | failure 43 | 44 | 45 | 46 | INIntentTitle 47 | Configuration 48 | INIntentTitleID 49 | gpCwrM 50 | INIntentType 51 | Custom 52 | INIntentVerb 53 | View 54 | 55 | 56 | INTypes 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Example/iOS_Widget/iOS_Widget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // iOS_Widget.swift 3 | // iOS_Widget 4 | // 5 | // Created by 赵新 on 2023/6/30. 6 | // Copyright © 2023 CocoaPods. All rights reserved. 7 | // 8 | 9 | import WidgetKit 10 | import SwiftUI 11 | import Intents 12 | 13 | struct Provider: IntentTimelineProvider { 14 | func placeholder(in context: Context) -> SimpleEntry { 15 | SimpleEntry(date: Date(), configuration: ConfigurationIntent()) 16 | } 17 | 18 | func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) { 19 | let entry = SimpleEntry(date: Date(), configuration: configuration) 20 | completion(entry) 21 | } 22 | 23 | func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline) -> ()) { 24 | var entries: [SimpleEntry] = [] 25 | 26 | // Generate a timeline consisting of five entries an hour apart, starting from the current date. 27 | let currentDate = Date() 28 | for hourOffset in 0 ..< 5 { 29 | let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! 30 | let entry = SimpleEntry(date: entryDate, configuration: configuration) 31 | entries.append(entry) 32 | } 33 | 34 | let timeline = Timeline(entries: entries, policy: .atEnd) 35 | completion(timeline) 36 | } 37 | } 38 | 39 | struct SimpleEntry: TimelineEntry { 40 | let date: Date 41 | let configuration: ConfigurationIntent 42 | } 43 | 44 | struct iOS_WidgetEntryView : View { 45 | var entry: Provider.Entry 46 | 47 | var body: some View { 48 | Text(entry.date, style: .time) 49 | } 50 | } 51 | 52 | struct iOS_Widget: Widget { 53 | let kind: String = "iOS_Widget" 54 | 55 | var body: some WidgetConfiguration { 56 | IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in 57 | iOS_WidgetEntryView(entry: entry) 58 | } 59 | .configurationDisplayName("My Widget") 60 | .description("This is an example widget.") 61 | } 62 | } 63 | 64 | struct iOS_Widget_Previews: PreviewProvider { 65 | static var previews: some View { 66 | iOS_WidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent())) 67 | .previewContext(WidgetPreviewContext(family: .systemSmall)) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Example/iOS_Widget/iOS_WidgetBundle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // iOS_WidgetBundle.swift 3 | // iOS_Widget 4 | // 5 | // Created by 赵新 on 2023/6/30. 6 | // Copyright © 2023 CocoaPods. All rights reserved. 7 | // 8 | 9 | import WidgetKit 10 | import SwiftUI 11 | 12 | @main 13 | struct iOS_WidgetBundle: WidgetBundle { 14 | var body: some Widget { 15 | iOS_Widget() 16 | iOS_WidgetLiveActivity() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Example/iOS_Widget/iOS_WidgetLiveActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // iOS_WidgetLiveActivity.swift 3 | // iOS_Widget 4 | // 5 | // Created by 赵新 on 2023/6/30. 6 | // Copyright © 2023 CocoaPods. All rights reserved. 7 | // 8 | 9 | import ActivityKit 10 | import WidgetKit 11 | import SwiftUI 12 | 13 | struct iOS_WidgetAttributes: ActivityAttributes { 14 | public struct ContentState: Codable, Hashable { 15 | // Dynamic stateful properties about your activity go here! 16 | var value: Int 17 | } 18 | 19 | // Fixed non-changing properties about your activity go here! 20 | var name: String 21 | } 22 | 23 | struct iOS_WidgetLiveActivity: Widget { 24 | var body: some WidgetConfiguration { 25 | ActivityConfiguration(for: iOS_WidgetAttributes.self) { context in 26 | // Lock screen/banner UI goes here 27 | VStack { 28 | Text("Hello") 29 | } 30 | .activityBackgroundTint(Color.cyan) 31 | .activitySystemActionForegroundColor(Color.black) 32 | 33 | } dynamicIsland: { context in 34 | DynamicIsland { 35 | // Expanded UI goes here. Compose the expanded UI through 36 | // various regions, like leading/trailing/center/bottom 37 | DynamicIslandExpandedRegion(.leading) { 38 | Text("Leading") 39 | } 40 | DynamicIslandExpandedRegion(.trailing) { 41 | Text("Trailing") 42 | } 43 | DynamicIslandExpandedRegion(.bottom) { 44 | Text("Bottom") 45 | // more content 46 | } 47 | } compactLeading: { 48 | Text("L") 49 | } compactTrailing: { 50 | Text("T") 51 | } minimal: { 52 | Text("Min") 53 | } 54 | .widgetURL(URL(string: "http://www.apple.com")) 55 | .keylineTint(Color.red) 56 | } 57 | } 58 | } 59 | 60 | struct iOS_WidgetLiveActivity_Previews: PreviewProvider { 61 | static let attributes = iOS_WidgetAttributes(name: "Me") 62 | static let contentState = iOS_WidgetAttributes.ContentState(value: 3) 63 | 64 | static var previews: some View { 65 | attributes 66 | .previewContext(contentState, viewKind: .dynamicIsland(.compact)) 67 | .previewDisplayName("Island Compact") 68 | attributes 69 | .previewContext(contentState, viewKind: .dynamicIsland(.expanded)) 70 | .previewDisplayName("Island Expanded") 71 | attributes 72 | .previewContext(contentState, viewKind: .dynamicIsland(.minimal)) 73 | .previewDisplayName("Minimal") 74 | attributes 75 | .previewContext(contentState, viewKind: .content) 76 | .previewDisplayName("Notification") 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Example/macOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // macOS 4 | // 5 | // Created by 赵新 on 2023/6/30. 6 | // Copyright © 2023 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @main 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | 15 | 16 | 17 | func applicationDidFinishLaunching(_ aNotification: Notification) { 18 | // Insert code here to initialize your application 19 | } 20 | 21 | func applicationWillTerminate(_ aNotification: Notification) { 22 | // Insert code here to tear down your application 23 | } 24 | 25 | func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { 26 | return true 27 | } 28 | 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /Example/macOS/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Example/macOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Example/macOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Example/macOS/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | Default 529 | 530 | 531 | 532 | 533 | 534 | 535 | Left to Right 536 | 537 | 538 | 539 | 540 | 541 | 542 | Right to Left 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | Default 554 | 555 | 556 | 557 | 558 | 559 | 560 | Left to Right 561 | 562 | 563 | 564 | 565 | 566 | 567 | Right to Left 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | -------------------------------------------------------------------------------- /Example/macOS/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // macOS 4 | // 5 | // Created by 赵新 on 2023/6/30. 6 | // Copyright © 2023 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import SnapKit 11 | import AxcUIKit 12 | import AxcBedrock 13 | 14 | class ViewController: NSViewController { 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | 18 | let label = AxcLabel() 19 | label.set(backgroundColor: AxcBedrockColor.white) 20 | // label._label.attributedStringValue = "Hello WorldHello WorldHello WorldHello WorldHello WorldHello World" 21 | // .axc.makeAttributed({ make in 22 | // make.set(font: 16) 23 | // .set(foregroundColor: AxcBedrockColor.black) 24 | // }) 25 | label.set(text: "Hello WorldHello WorldHello WorldHello WorldHello WorldHello World") 26 | label.set(textFont: 16.axc.nsFont(weight: .bold)) 27 | label.set(contentEdgeInsets: 8.axc.edge) 28 | // label.set(textBackgroundColor: "FF0000") 29 | // label.set(numberOfLineType: .infinite) 30 | // label.set(textLineBreakMode: .byTruncatingTail) 31 | view.addSubview(label) 32 | 33 | label.snp.makeConstraints { make in 34 | make.top.right.equalToSuperview() 35 | make.width.equalTo(120) 36 | // make.height.equalTo(120) 37 | } 38 | 39 | AxcGCD.Delay(delay: 2) { 40 | label.set(lineSpacing: 20) 41 | label.set(textBackgroundColor: AxcBedrockColor.green) 42 | print("延迟结束") 43 | } 44 | AxcGCD.Delay(delay: 3) { 45 | label.set(text: "Hello WorldHello WorldHello World") 46 | print("延迟结束") 47 | } 48 | AxcGCD.Delay(delay: 4) { 49 | label.set(lineSpacing: 8) 50 | label.set(textBackgroundColor: AxcBedrockColor.green) 51 | print("延迟结束") 52 | } 53 | 54 | let textField = _AxcNSLabel() 55 | textField.backgroundColor = .lightGray 56 | textField.attributedStringValue = "Hello WorldHello WorldHello WorldHello WorldHello WorldHello World" 57 | .axc.makeAttributed({ make in 58 | make.set(font: 16) 59 | }) 60 | 61 | let contentView = AxcView() 62 | contentView.set(backgroundColor: NSColor.white) 63 | contentView.addSubview(textField) 64 | textField.snp.makeConstraints { make in 65 | make.edges.equalToSuperview().inset(8.axc.edge) 66 | } 67 | 68 | let testView = AxcView() 69 | testView.addSubview(contentView) 70 | contentView.snp.makeConstraints { make in 71 | make.edges.equalToSuperview().inset(8.axc.edge) 72 | } 73 | 74 | view.addSubview(testView) 75 | testView.snp.makeConstraints { make in 76 | make.top.left.equalToSuperview() 77 | make.width.equalTo(120) 78 | } 79 | 80 | AxcGCD.Delay(delay: 2) { 81 | textField.attributedStringValue = textField.attributedStringValue.axc.makeAttributed { make in 82 | make.set(paragraphStyle: NSMutableParagraphStyle.Axc.CreateParagraphStyle({ make in 83 | make.set(lineSpacing: 20) 84 | })) 85 | } 86 | print("延迟结束") 87 | } 88 | } 89 | 90 | override var representedObject: Any? { 91 | didSet { 92 | // Update the view, if already loaded. 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Example/macOS/macOS.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/macOSTests/macOSTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // macOSTests.swift 3 | // macOSTests 4 | // 5 | // Created by 赵新 on 2023/6/30. 6 | // Copyright © 2023 CocoaPods. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import macOS 11 | 12 | final class macOSTests: XCTestCase { 13 | 14 | override func setUpWithError() throws { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDownWithError() throws { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() throws { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | // Any test you write for XCTest can be annotated as throws and async. 26 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. 27 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. 28 | } 29 | 30 | func testPerformanceExample() throws { 31 | // This is an example of a performance test case. 32 | self.measure { 33 | // Put the code you want to measure the time of here. 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 AxcLogo 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## AxcUIKit 3 | 4 | AxcUIKit将按照统一的规格进行封装,计划未来将支持Dart、Swift、kotlin等一系列前端语言 5 | 并且语法会做到尽量统一,尽可能的做到跨平台开发的流畅性和熟悉性 6 | 7 | ## 说明 8 | 9 | 目前该库正在进行重构中,所有控件都将改为子组件按需导入,并且做细化颗粒度解耦。 10 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------