macos swift_swift macos - CSDN
  • 开源macOS应用大全
  • Apple发布官方SwiftUI 制作macOS App教程 在创建了适用于watchOS的Landmarks应用程序的版本之后,是时候将您的目光投向更大的东西了:将Landmarks引入Mac。您将以到目前为止所学的内容为基础,以完善构建适用于iOS...
        

    Apple发布官方SwiftUI 制作macOS App教程

    在创建了适用于watchOS的Landmarks应用程序的版本之后,是时候将您的目光投向更大的东西了:将Landmarks引入Mac。您将以到目前为止所学的内容为基础,以完善构建适用于iOS,watchOS和macOS的SwiftUI应用的经验。
    首先,将macOS目标添加到项目中,然后重新使用为iOS应用创建的共享数据。拥有所有资产后,您将创建SwiftUI视图以在macOS上显示列表视图和详细视图。
    请按照以下步骤构建该项目,或下载完成的项目以自行探索。

    教程地址

    https://developer.apple.com/tutorials/swiftui/creating-a-macos-app

    效果

    41085-e5068d0da5f1f338.jpg
    Jietu20200205-205003@2x.jpg

    更多SwiftUI教程和代码关注专栏

    QQ:3365059189
    SwiftUI技术交流QQ群:518696470

    展开全文
  • 用于在macOS Mojave上运行SwiftUI教程代码的Playground
  • 在我对 macos App 进行本地化的过程中,遇到不少坑。不过最后还是搞定了,在这里记录下来结果,供参考。 开始之前,我想先说说NSString,看到很多人似乎更愿意使用NSString的localizedStringWithFormat 函数。但在我...

    在我对 macos App 进行本地化的过程中,遇到不少坑。不过最后还是搞定了,在这里记录下来结果,供参考。
    开始之前,我想先说说NSString,看到很多人似乎更愿意使用NSString的localizedStringWithFormat 函数。但在我眼里,NSString 有些不伦不类。既然swift有String,那就是说,NSString应该退出历史舞台了,至少我这么看。所以只要有可能,我几乎从来不用NSString。
    下面正式开始。
    本地化或者叫做国际化,实际就是让你的APP能在多语言环境下本地化使用,直白说,就是“汉化”这个词的意思。如果想让世界上更多人使用你的APP,你应该让她们看到自己母语环境,这没什好说的。
    在一个APP中,本地化工作主要分三个部分。一部分是界面,.xib, .storyboard这类东西,另一个是资源,主要指图片之类的,第三个是字符串。也就是在你程序各处用到的向用户显示出来的字符串。需要注意的是,用户看不到的部分不属于要本地化的内容。
    前两个部分都很容易,至于Assets.xcasserts这个东西我没闹清楚,所以略去不说。本文主要说字符串。
    在说这个问题之前,应该插一句:添加语言。App只支持你添加过的语言。
    缺省语言通常是英语,如果要增加对中文的支持,请按下面操作。
    在Xcode编程环境,选择 show the project nivagator 选项卡。
    右边选择info选项卡,下方你会看到 localizations,项目,下面的 + 号,可以添加新语言。大约有上百种不同的语言可供支持。
    支持多种语言,在你进行本地化的时候,资源就会生成对应的本地化资源。你需要对这些资源分别处理,这样才能生成国际化程序。
    字符串资源往往保存在属性列表里面。有两种属性列表,一种是srings 扩展名的,另外一种是stringsdict作为扩展名的。
    内容字符串也有两种,一种是词汇或者短语的直接翻译,一种是格式化字符串,例如量词的使用,日期时间,单数复数表示,钱币的表示等等。 后面的情况往往需要一个参数,%d 这样的,这个估计大家都知道。
    一句话或者一个词的翻译看起来很简单,但是考虑到高要求,情况就会变的相当复杂。比如时间,一天两三三天,即使在中文里面7天,说成“一周”是不是更好?还有就是界面上,有时空间比较大,可以用长而清晰的表述,而界面上空间比较狭窄的情况下,就只能长话短说。考虑各个国家语言特定之处,问题就会变的更复杂。
    让我们归纳一下:字符串有两种:一种是一对一字符串,另一种是一对多字符串,分别对应Localizable.strings和Localizable.stringsdict。对于后一种情况,Apple给出的最新解决办法是 plural variants(复数变体)。
    “ Localizable” 这个名字具有特殊的意义,这是Xcode系统内置的名称。也就是说,NSLocalizedString 会自动查找这个名字的字符串资源,无论是 Localizable.strings 还是 Localizable.stringsdict。 其他名称择不会自动自动调用。
    如果要添加Localizable.strings,选择新建,文件类型选择strings类型,文件名必须使用 Localizable,Localizable.stringsdict 资源的添加也一样,只是类型选择stringsdict。
    添加之后,选中这个文件,按照上图点击localize…, 然后选中那些语言, 根据你添加的语言左边会如下图显示。这个就就可以修改不同语言对应的字符串资源了。
    至此你的准备工作已经做好了。
    接下来开始第二部分。回到应用程序本身,比如说,你在打开对话框中使用了一句话:“一次只能打开一个单独的图片文件”作为提示。那么做英语版本这句话可能是这样的“Single image file can be opened at a time”。
    这句话是一对一的关系,所以我们放在Localizable.strings 中处理。那么我们分别需要分别在两个文件中加入这样一行:
    英文:
    “OpenPromptText” = "Single image file can be opened at a time”;

    汉语:
    “OpenPromptText” = “一次只能打开一个单独的图片文件";

    在代码里面使用这样的语句可以获得字符串。

    let promptText = NSLocalizedString("OpenPromptText", comment: “”)
    

    注意:键名大小写敏感。
    提醒一下,要对本地化进行测试。你需要按着option键,点击工具条左侧大于号后面的My Mac 图标。修改Application Language 和 Application Region,就可以进行响应语言的测试了。
    其他需要本地化的地方依次进行。
    最后,我们再谈谈 plural variants, 这可是本地化的重头戏。首先,什么事复数变体呢?直观上看,就是有多个变体的意思,也就是一种情况根据条件的不同会有多个不同的结果。
    如果你没有遇到过这种情况可能会觉得很抽象不容易懂,其实并没有那么复杂。就是同一件事的不同描述,比方说你的女朋友,她的父母会称她为女儿,她的爷爷奶奶会称她为孙女,而她的同事可能会称号她的名字,而她的玩伴可能会称呼她的乳名。
    为了减少打字时间,我用两个现成的例子来说明这种用法。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>GDP</key>
        <dict>
            <key>NSStringVariableWidthRuleType</key>
            <dict>
                <key>1</key>
                <string>GDP (billions)</string>
                <key>20</key>
                <string>GDP (in billions)</string>
                <key>50</key>
                <string>Gross Domestic Product (in billions)</string>
            </dict>
        </dict>
    
    	<key>%d record trovati</key>
    	<dict>
    		<key>NSStringLocalizedFormatKey</key>
    		<string>%#@num_people_in_room@ in the room</string>
    		<key>num_people_in_room</key>
    		<dict>
    			<key>NSStringFormatSpecTypeKey</key>
    			<string>NSStringPluralRuleType</string>
    			<key>NSStringFormatValueTypeKey</key>
    			<string>d</string>
    			<key>zero</key>
    			<string>No record</string>
    			<key>one</key>
    			<string>Only one record</string>
    			<key>other</key>
    			<string>Some records</string>
    		</dict>
    	</dict>
    </dict>
    </plist>
    

    第一种情况是界面上空白位置的大小,对应不同长度的显示文本。 就是常说的GDP,显示的短一点长一点。第二种,则是根据数量换成含义更为模糊的语言描述。

    在代码里面是这样获得本地化字符串的。
    let gdpText = String.localizedStringWithFormat(NSLocalizedString(“GDP”, comment: “”),25)
    let recordText = String.localizedStringWithFormat(NSLocalizedString("%d record trovati", comment: “”), 2)

    下面我们来解释一下各个方面的内容。从后往前吧,上面两个表达式的意义是,

    记住:键名可以包含%号。
    %d record trovati 这个键名,即使把键名里面的%d去掉,并不影响结果。但是如果不用 NSLocalizedString,直接给出字符串的键名,则计算失败。这说明了一件事,尽管 NSLocalizedString 只是返回一个字符串,但还有别的事情发生,或许就是当前键值吧。这个深究也没意思了。

    那个GDP的输入参数,我尝试不给出它所要的数值,依然可以得到结果。似乎是从0开始的数据段的概念,而不是一个值。

    另外变量:GDP那个陷入没有明确接收变量,但变量也起作用了。

    下面谈谈: NSStringFormatSpecTypeKey, NSStringLocalizedFormatKey,
    NSStringPluralRuleType
    NSStringFormatValueTypeKey
    NSStringVariableWidthRuleType
    这些值目前没有找到文档说明。

    再来看看 %d record trovati, 这个其实是键值,也就是名称。这个名称其实是任意的,不一定非得是这个。他对应的字符串是一个格式化字符串:%#@num_people_in_room@ in the room。 两个@之间是变量名称,接下来的num_people_in_room说的就是这个复数变体,它不同的值会对应不同的结果值。最后,用这个结果值去替换格式化字符串的变量。
    GDP 这个复数变体,没有格式化字符串。它隐含的输入值,对应下面字典得到结果就是最后的结果值。

    展开全文
  • 使用swift通过14个迷你项目来助推macOS开发
  • 原作于1年前, 用USB转串口连接TFmini(北醒光子的一款Lidar)和macOS. 基于Xcode 9, Swift 4, ORSSerialPort开发. 安装USB转串口驱动 常用的USB转串口芯片有 CH341, CP210X, PL2303, FT232等, 点击相应的名称下载...

    原作于1年前, 用USB转串口连接TFmini(北醒光子的一款Lidar)和macOS. 基于Xcode 9, Swift 4, ORSSerialPort开发.


    安装USB转串口驱动

    常用的USB转串口芯片有 CH341, CP210X, PL2303, FT232等, 点击相应的名称下载macOS驱动并安装. 我这里使用的是CP2104. 安装完成后连接TFmini到macOS:
    在这里插入图片描述

    安装USB转串口驱动后, 可以在Release中直接下载 .dmg 安装使用. 如果想知道工程怎么构建的, 可以往下读.


    新建Xcode9/Swift4工程

    新建Cocoa App工程, 命名为TFmini, 不勾选Storyboard.

    关闭工程, 打开macOS终端, 切换到TFmini Xcode工程目录, 创建Podfile, 然后用文本编辑打开:

    touch Podfile
    open -a TextEdit Podfile
    

    填入以下代码:

    target "TFmini"
    pod "ORSSerialPort"
    

    其中target后面是工程名, 保存关闭.

    使用Cocoapods导入:

    pod install
    

    完成后关闭终端, 打开工程目录下的 .xcworkspace 文件. 点击顶部的黄色小叹号, 更新到推荐的设置.

    手动添加OC到Swift的桥接文件: TFmini-Bridging-Header.h, 其中TFmini是工程名, 内容为:

    #import "ORSSerialPort.h"
    #import "ORSSerialPortManager.h"
    

    依次点击 工程名 -> Targets下的工程名 -> Build Setting -> Swift Compiler -General -> Objective-C Bridging Header, 双击右侧空白处, 填入以下代码后回车:

    $(PROJECT_DIR)/$(PROJECT_NAME)/$(PROJECT_NAME)-Bridging-Header.h
    

    如图所示:

    在这里插入图片描述

    新建一个TFmini类, 继承于NSObject.

    然后在XIB文件中加入一个 Object, 在Identity Inspector中的Class, 选择刚刚创建的TFmini类, 这样, 就可以关联对象到TFmini类中了:

    在这里插入图片描述

    拖Label, Pop Up Button, Push Button, TextView各种控件到Window的View中, 给控件添加一些约束, 并设置Window的最小尺寸为480*360:

    在这里插入图片描述

    关联Open按钮和TextView接收框到TFmini中, 关联Open按钮的点击事件, 注意 NSTextView, 需要连点3下才能选中, 别拖成ScrollView或者ClipView了:

        @IBOutlet weak var openCloseButton: NSButton!
        @IBOutlet var receivedDataTextView: NSTextView!
        
        @IBAction func openOrClosePort(_ sender: Any) {
        }
    

    把其余的代码添加进来, 主要是 ORSSerialPortDelegate, NSUserNotificationCenterDelegate的一些实现.
    变量前的 @objc dynamic 是后面的Binding必须的.
    TFmini的数据解析在 func serialPort(_ serialPort: ORSSerialPort, didReceive data: Data) 中, 数据接收超过10000帧, 会清空缓存.
    参考工程源文件.

    接下来就是Binding了.

    串口弹出按钮的Binding:

    在这里插入图片描述

    波特率弹出按钮的Binding:

    在这里插入图片描述

    打开关闭按钮的Binding, 保证没有串口时不可被选中:

    在这里插入图片描述

    设置完毕, 运行, 串口号选择SLAB_USBtoUART, 波特率选择115200, open报错:

    SerialPort SLAB_USBtoUART encountered an error: Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted" UserInfo={NSFilePath=/dev/cu.SLAB_USBtoUART, NSLocalizedDescription=Operation not permitted}
    

    修改 工程名.entitlements, 增加 com.apple.security.device.serial, 设为YES, 如果想要应用上传到AppStore, 这个是必须的:

    在这里插入图片描述

    确保Team有效:

    在这里插入图片描述

    依次点击工程名 -> Targets下的工程名 -> Capbilities -> Keychain Sharing -> 打开开关.

    再次运行, 就可以了:

    在这里插入图片描述
    左边是原始的9字节十六进制数, 右边是计算出来的实际距离值, 单位cm.

    Github链接

    https://github.com/TFmini/TFmini-macOS

    展开全文
  • macOS应用开发基础教程 原书 pdf 并附带Swift 4的源代码
  • macOS的menuBar功能非常强大,我们在开发macOS应用的时候,经常需要利用menuBar实现功能。然而网上关于menuBar开发的文档却甚少,更别提用上SwiftUI开发的。这篇文章从以下几部分讲解用SwiftUI开发menuBar/statusBar...

    macOS的menuBar功能非常强大,我们在开发macOS应用的时候,经常需要利用menuBar实现功能。然而网上关于menuBar开发的文档却甚少,更别提用上SwiftUI开发的。这篇文章从以下几部分讲解用SwiftUI开发menuBar/statusBar的方法,希望对大家有帮助:

    一、在statusBarItem弹出Popover

    二、更改Popover背景色

    三、在statusBar上控制主窗口的开关

    四、在statusBar上控制statusBar的显隐/开关

    一、在statusBarItem弹出Popover

    statusBar弹出menu是比较简单的,但是如果我们要做一些定制,就需要用到Popover了

    创建Popover

    在AppDelegate文件中,声明一个NSPopover变量(下面的statusBarItem之后会用到)

    var popover: NSPopover! var statusBarItem: NSStatusItem!
    
    

    随后,在applicationDidFinishLaunching中, ContentView 初始化之后,加上

    // Create the popover 
    let popover = NSPopover() 
    popover.contentSize = NSSize(width: 400, height: 400) 
    popover.behavior = .transient //代表用户点击其他区域时popover自动消失 popover.contentViewController = NSHostingController(rootView: contentView) self.popover = popover

    此时我们的AppDelegate文件应该长这样:

    import Cocoa
    import SwiftUI
    
    @NSApplicationMain
    class AppDelegate: NSObject, NSApplicationDelegate {
    
        var popover: NSPopover!
        
        func applicationDidFinishLaunching(_ aNotification: Notification) {
            // Create the SwiftUI view that provides the window contents.
            let contentView = ContentView()
    
            // Create the popover
            let popover = NSPopover()
            popover.contentSize = NSSize(width: 400, height: 500)
            popover.behavior = .transient
            popover.contentViewController = NSHostingController(rootView: contentView)
            self.popover = popover
        }
        
    }

     

    这样我们即创建了一个Popover视图以及它的控制器,这个Popover的视图内容是我们的ContentView.swift里的内容。这样我们就可以在ContentView里用SwiftUI自由地定制我们的Popover。接下来我们要做的是,创建一个menuBarItem,当用户点击menuBarItem的按钮时,弹出我们已经写好的Popover。

    self.statusBarItem = NSStatusBar.system.statusItem(withLength: CGFloat(NSStatusItem.variableLength))
    if let button = self.statusBarItem.button {
         button.image = NSImage(named: "Icon")
         button.action = #selector(togglePopover(_:))
    }

    还记得我们一开始声明了

    var statusBarItem: NSStatusItem!

    现在我们需要创建它,同时给它在menuBar设置个图标,绑定点击事件为togglePopover

    self.statusBarItem = NSStatusBar.system.statusItem(withLength: CGFloat(NSStatusItem.variableLength))
    if let button = self.statusBarItem.button {
         statusBarItem.button?.title = "⏳"
         button.action = #selector(togglePopover(_:))
    }

    绑定togglePopover是为了在用户点击时弹出Popover

    // Create the status item
    @objc func togglePopover(_ sender: AnyObject?) {
         if let button = self.statusBarItem.button {
              if self.popover.isShown {
                   self.popover.performClose(sender)
              } else {
                   self.popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
              }
         }
    }

    最终,我们的AppDelegate文件长这样

    import Cocoa
    import SwiftUI
    
    @NSApplicationMain
    class AppDelegate: NSObject, NSApplicationDelegate {
    
        var popover: NSPopover!
        var statusBarItem: NSStatusItem!
        
        func applicationDidFinishLaunching(_ aNotification: Notification) {
            // Create the SwiftUI view that provides the window contents.
            let contentView = ContentView()
    
            // Create the popover
            let popover = NSPopover()
            popover.contentSize = NSSize(width: 400, height: 500)
            popover.behavior = .transient
            popover.contentViewController = NSHostingController(rootView: contentView)
            self.popover = popover
            
            // Create the status item
            self.statusBarItem = NSStatusBar.system.statusItem(withLength: CGFloat(NSStatusItem.variableLength))
            
            if let button = self.statusBarItem.button {
                statusBarItem.button?.title = "⏳"
                button.action = #selector(togglePopover(_:))
            }
        }
        
        @objc func togglePopover(_ sender: AnyObject?) {
            if let button = self.statusBarItem.button {
                if self.popover.isShown {
                    self.popover.performClose(sender)
                } else {
                    self.popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
                }
            }
        }
        
    }
    

     

     

     

    二、更改Popover背景色

    我们创建出来的Popover默认的背景色是透明的,很容易与我们的UI不搭配。如果需要定制背景颜色,目前我没有发现直接用SwiftUI实现的方法。我们需要去拓展NSPopover类,在新建实例的时候指定背景色。

    首先,拓展NSPopover类:

    import Cocoa
    import SwiftUI
    extension NSPopover {
        
        private struct Keys {
            static var backgroundViewKey = "backgroundKey"
        }
        
        private var backgroundView: NSView {
            let bgView = objc_getAssociatedObject(self, &Keys.backgroundViewKey) as? NSView
            if let view = bgView {
                return view
            }
            
            let view = NSView()
            objc_setAssociatedObject(self, &Keys.backgroundViewKey, view, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            NotificationCenter.default.addObserver(self, selector: #selector(popoverWillOpen(_:)), name: NSPopover.willShowNotification, object: nil)
            return view
        }
        
        @objc private func popoverWillOpen(_ notification: Notification) {
            if backgroundView.superview == nil {
                if let contentView = contentViewController?.view, let frameView = contentView.superview {
                    frameView.wantsLayer = true
                    backgroundView.frame = NSInsetRect(frameView.frame, 1, 1)
                    backgroundView.autoresizingMask = [.width, .height]
                    frameView.addSubview(backgroundView, positioned: .below, relativeTo: contentView)
                }
            }
        }
        
        var backgroundColor: NSColor? {
            get {
                if let bgColor = backgroundView.layer?.backgroundColor {
                    return NSColor(cgColor: bgColor)
                }
                return nil
            }
            set {
                backgroundView.wantsLayer = true
                backgroundView.layer?.backgroundColor = newValue?.cgColor
            }
        }
        
    }

    在AppDelegate初始化时,增加background参数

    let popover = NSPopover()
            popover.backgroundColor = NSColor.white   //设置popover颜色,对应extension里对popover的改写
            popover.contentSize = NSSize(width: 350, height: 400)

    搞定!

    三、在statusBar增加控制主窗口开关和插件开关

    现在statusBarItem的视图文件中加入按钮

    HStack{
                    Text("Time Capsule").font(Font.system(.headline)).bold()
                    Spacer()
                    MenuButton(label: Image("moreBlack").resizable().frame(width:20,height:20)) {
                        Button(action: {
                          showWindow()
                        }) {
                            Text("打开主窗口")
                        }
                        Button(action: {
                            hideStatusBar()
                        }) {
                            Text("退出")
                        }
                    }.menuButtonStyle(BorderlessButtonMenuButtonStyle())
                    
                }
                .padding([.top, .trailing], 18).padding(.leading,30)

    接下来我们要补充两个按钮点击事件showWindow()和hideStatusBar()的方法

    func showWindow() {
        NSApp.unhide(nil)
    
    func hideStatusBar() {
        let appDelegate = NSApplication.shared.delegate as! AppDelegate
        appDelegate.statusBarItem.isVisible = false
    }

    NSApp.unhide方法可以直接显示我们的主程序,hide则可以隐藏主程序

    statusBarItem.isVisible设为false则可以直接让我们的插件消失

    展开全文
  • MacOS上键盘/鼠标控制应用的Swift语言开发笔记 背景 继续在做小gadget,先学习基本操作。 这次需要实现的功能是程序控制键盘和鼠标,也就是人不需要碰键盘鼠标而键盘自动输入,鼠标自动移动点击的功能。 网上...
  • macOS的menuBar功能非常强大,我们在开发macOS应用的时候,经常需要利用menuBar实现功能。这篇文章主要是关于如何用swiftUI优雅地现在menuBar的popover。 最终效果图: 创建Popover 在AppDelegate文件中,...
  • 原文地址:http://footle.org/WeatherBar/ 下面开始介绍如何使用Swift开发一个Mac Menu Bar(Status Bar) App。通过做一个简单的天气app。天气数据来源于OpenWeatherMap 完成后的效果如下: 一、开始建立工程打开...
  • Swift macOS 点击窗口关闭按钮,退出程序 在项目中的 AppDelegate 文件中添加如下方法 // 意思是,当关闭app的最后一个窗口时,退出 app func applicationShouldTerminateAfterLastWindowClosed(_ sender: ...
  • 本文价值与收获 看完本文后,您将能够作出...import SwiftUI // 1、主界面 struct ContentView: View { @State var selection: Set<Int> = [0] var body: some View { //2.左侧Siderbar NavigationView {
  • 我们希望设置一下list的背景和行高,在macOS中该如何实现呢 本文价值与收获 看完本文后,您将能够作出下面的界面 看完本文您将掌握的技能 macOS下list基础使用 设置list背景 设置list行高 代码 import SwiftUI ...
  • 本文价值与收获 看完本文后,您将能够作出下面的界面 Jietu20200601-195635@2x.jpg ...import SwiftUI struct ContentView: View { @State private var emailAddress = "" var body: some Vie...
  • SwiftUI macOS 如何新建一个Windows窗口并显示 教程含源码 实战需求 SwiftUI macOS 如何新建一个Windows窗口并显示 ? 本文价值与收获 看完本文后,您将能够作出下面的界面 看完本文您将掌握的技能 掌握创建新...
  • 实战需求 ...本文价值与收获 看完本文后,您将能够作出下面的界面 代码 1、主界面 struct ContentView: View { var body: some View { NavigationView { List(["Hello", "World"],id: \.self) { str in ...
  • 方法一:func applicationShouldHandleReopen(sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool { if !flag{ let sb = NSStoryboard(name: "Main", bundle: nil) let controller = sb?
  • 窗口大小的设置主要在AppDelegate.swift中配置 设置默认窗口大小 通过windows.setFrame方法设置窗口大小 func applicationDidFinishLaunching(_ aNotification: Notification) { // Create the SwiftUI view ...
  • macOS开发入门教程: Part 3

    千次阅读 2018-03-24 10:19:52
    原文:How To Make A UIViewController Transition Animation Like in the Ping App ... 更新说明:本教程由 Luke Parhm 更新至 Xcode 9/Swift 4。原文作者是 Rounak Jain。 不久前,匿名社交网络 app Secret 的...
1 2 3 4 5 ... 20
收藏数 4,625
精华内容 1,850
关键字:

macos swift