anyHouse-iOS 高仿ClubHouse
前言
Clubhouse是一个新的社交网络应用程序,提供了实时音频聊天互动方式,给用户创造了打破由社会圈层壁垒所导致的信息传播和人际链接壁垒的可能性。Clubhouse通常被昵称为“硅谷最热门的初创企业”,将自己定位为一个“独家”和“另类”社交网络,吸引了各种名人和只想互相交谈的人。
App Store 下载地址
Github开源下载地址
开发环境
- 开发工具:Xcode12 真机运行
- 开发语言:Swift
- SDK:ARtcKit_iOS
效果展示
核心框架
platform :ios, '9.0'
use_frameworks!target 'anyHouse-iOS' do#anyRTC 音视频库pod 'ARtcKit_iOS', '~> 4.1.4.1'#anyRTC 实时消息库pod 'ARtmKit_iOS', '~> 1.0.1.4'
end
项目文件目录结构
功能目录:
Main:
①ARMainViewController:主页面,房间列表;
②ARMineViewController:我的,包含修改昵称、隐私协议、版本信息等等;
③ARCreateRoomViewController:创建房间,包含创建公开/私密房间、添加话题。
Audio:
①ARAudioViewController:语音房间,包含语音聊天、上下麦等功能;
②ARMicViewController:请求连麦列表;
③ARReportViewController:举报功能。
项目部分功能模块详解
登录、我的、首页
- 首页
override func viewDidLoad() {super.viewDidLoad()// Do any additional setup after loading the view.let avatar = Int(UserDefaults.string(forKey: .avatar) ?? "1")! - 1avatarButton.setImage(UIImage(named: headListArr![avatar] as! String), for: .normal)let arr = UserDefaults.standard.array(forKey: blacklistIdentifier)arr?.count ?? 0 > 0 ? (blackList.addObjects(from: arr!)) : niltableView.tableFooterView = UIView()tableView.separatorStyle = .nonetableView.rowHeight = UITableView.automaticDimensiontableView.estimatedRowHeight = 120tableView.mj_header = MJRefreshNormalHeader(refreshingBlock: {[weak self] () -> Void inguard let weakself = self else {return}weakself.index = 1weakself.requestRoomList()})}@objc func createPlaceholder() {placeholderView.showPlaceholderView(self.tableView, placeholderImageName: "icon_add", placeholderTitle: "可以尝试下拉刷新或者创建房间") {self.tableView.mj_header?.beginRefreshing()}placeholderView.backgroundColor = UIColor.clear}
创建房间、添加话题
- 创建房间逻辑:
@IBAction func didClickTopicButton(_ sender: Any) {passwordTextField.resignFirstResponder()let alertVc = ARAlertTextViewController(title: "添加话题 \n ", message: "比如发生在身边的趣事", preferredStyle: .alert)alertVc.updateTextView(text: topic)let cancelAction = UIAlertAction (title: "取消" , style: .cancel , handler: nil )let okAction = UIAlertAction (title: "设置话题" , style: . default , handler: {action inif !self.stringAllIsEmpty(string: alertVc.textView.text) {self.topic = alertVc.textView.text ?? ""self.updateTopic()}})alertVc.addAction(cancelAction)alertVc.addAction(okAction)present(alertVc, animated: true, completion: nil)DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {alertVc.textView.becomeFirstResponder()}}func updateTopic() {if isPrivate == 0 {(topic.count == 0) ? (topicLabel.text = publicText) : (topicLabel.text = String(format: "%@:“%@”", publicText,topic))} else {(topic.count == 0) ? (topicLabel.text = passwordText) : (topicLabel.text = String(format: "%@:“%@”", passwordText,topic))}}@IBAction func didClickButton(_ sender: UIButton) {if sender.tag != isPrivate {isPrivate = sender.tagpasswordTextField.resignFirstResponder()updateTopic()if isPrivate == 0 {//公开passwordView.isHidden = truepadding.constant = 0publicButton.backgroundColor = UIColor(hexString: "#DFE2EE")passwordButton.backgroundColor = UIColor.white} else {//私密passwordView.isHidden = falsepadding.constant = 47passwordButton.backgroundColor = UIColor(hexString: "#DFE2EE")publicButton.backgroundColor = UIColor.white}}}
- 重写UIAlertController实现添加话题:
class ARAlertTextViewController : UIAlertController, UITextViewDelegate {public var textView : UITextView!private var tipLabel: UILabel!override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)let contentView = UIView()let controller = UIViewController()controller.view = contentViewtextView = UITextView()textView.delegate = selftextView.layer.masksToBounds = truetextView.layer.cornerRadius = 5contentView.addSubview(textView)textView.snp.makeConstraints({ (make) inmake.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 15, bottom: 16, right: 15))})tipLabel = UILabel.init()tipLabel.text = "还剩输入60个字符"tipLabel.textColor = UIColor(hexString: "#999999")tipLabel.font = UIFont.init(name: "PingFang SC", size: 12)tipLabel.textAlignment = .centertextView.addSubview(tipLabel)tipLabel.snp.makeConstraints({ (make) inmake.bottom.equalTo(textView.snp_bottom).offset(80)make.centerX.equalToSuperview()make.width.equalTo(100)make.height.equalTo(15)})//super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)self.setValue(controller, forKey: "contentViewController")}func updateTextView(text: String!) {textView.text = texttipLabel.text = String(format: "还剩输入%d个字符", 60 - text.count)}required init?(coder aDecoder: NSCoder) {fatalError("init(coder:) has not been implemented")}func textViewDidChange(_ textView: UITextView) {if textView.text?.count ?? 0 > 60 {textView.text = String(textView.text.prefix(60))}tipLabel.text = String(format: "还剩输入%d个字符", 60 - textView.text.count)}
}
语音房间、互动连麦
- 核心代码:
func initializeEngine() {// init ARtcEngineKitrtcKit = ARtcEngineKit.sharedEngine(withAppId: UserDefaults.string(forKey: .appId)!, delegate: self)rtcKit.setAudioProfile(.musicHighQuality, scenario: .gameStreaming)//开启音频AI降噪let dic1: NSDictionary = ["Cmd": "SetAudioAiNoise", "Enable": 1]rtcKit.setParameters(getJSONStringFromDictionary(dictionary: dic1))rtcKit.setChannelProfile(.liveBroadcasting)if infoModel!.isBroadcaster {rtcKit.setClientRole(.broadcaster)}rtcKit.enableAudioVolumeIndication(500, smooth: 3, report_vad: true)//init ARtmKitrtmEngine = ARtmKit.init(appId: UserDefaults.string(forKey: .appId)!, delegate: self)rtmEngine.login(byToken: infoModel?.rtmToken, user: UserDefaults.string(forKey: .uid) ?? "0") { [weak self](errorCode) inself?.rtmChannel = self?.rtmEngine.createChannel(withId: (self?.infoModel?.roomId)!, delegate: self)self?.rtmChannel?.join(completion: { (errorCode) in})}}
- 音频检测
//提示频道内谁正在说话、说话者音量及本地用户是否在说话的回调func rtcEngine(_ engine: ARtcEngineKit, reportAudioVolumeIndicationOfSpeakers speakers: [ARtcAudioVolumeInfo], totalVolume: Int) {for speakInfo in speakers {if speakInfo.volume > 3 {for index in 0..<modelArr[0].count {let micModel = modelArr[0][index]if speakInfo.uid == micModel.uid || (speakInfo.uid == "0" && micModel.uid == UserDefaults.string(forKey: .uid)){let indexPath: NSIndexPath = NSIndexPath(row: index, section: 0)let cell: ARAudioViewCell? = collectionView.cellForItem(at: indexPath as IndexPath) as? ARAudioViewCellcell?.startAnimation()break}}}}}
- 上下麦
private func becomBroadcaster(role: ARClientRole) {//切换角色rtcKit.setClientRole(role)if role == .audience {//下麦audioButton.isHidden = trueaudioButton.isSelected = falsemicButton.isHidden = falsemicButton.isSelected = falsertcKit.enableLocalAudio(true)for index in 0..<modelArr[0].count {let micModel = modelArr[0][index]if micModel.uid == UserDefaults.string(forKey: .uid) {modelArr[0].remove(at: index)modelArr[1].append(micModel)collectionView.reloadData()break}}Drop.down("您已成为听众", state: .color(UIColor(hexString: "#4BAB63")), duration: 1)} else {//上麦audioButton.isHidden = falsemicButton.isHidden = true}}
协议、屏蔽、举报功能
- 为应对苹果审核机制,故而添加协议、屏蔽、举报等功能模块。
RTM相关信令
json:key =action value Int
例如: {“action”:1} toID:发送对象
Key | Value | 说明 | http |
---|---|---|---|
action
userName avatar |
1
userName(String) 1(Int) |
举手 (toID为主持人) |
updateUserStatus
status =1 |
action | 2 | 邀请听众上台 (toID为该听众) |
updateUserStatus
status =-1 |
action |
3
userName(String) |
听众拒绝邀请(toID为主持人) |
updateUserStatus
status = 0 |
action
userName |
4 | 同意邀请(toID为主持人) |
updateUserStatus
status =2 |
action | 5 | 主持人关闭该发言者的麦克风(toID为该听众) | |
action | 6 | 主持人将该发言者设置为听众(下台)(toID为该听众) |
updateUserStatus
status =0 |
action | 7 | 取消举手(toID为主持人) |
updateUserStatus
status = 0 |
action | 8 | 主持人正常离开(发送频道消息) | leaveRoom |
action
userName avatar |
9
userName(String) 1(Int) |
加入rtm频道发送个人信息(发送频道消息) |
加入频道发送频道消息,用于其他人显示
{“avatar”:1,userName:“lili”}
结束语
本项目并没有完全复原ClubHouse,项目中还存在一些bug和待完善的功能点。仅供参考,欢迎大家fork。有不足之处欢迎大家指出issues。
最后再贴一下 Github开源下载地址 。如果觉得不错,希望点个star~
anyHouse-iOS 高仿ClubHouse相关推荐
- iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
2019独角兽企业重金招聘Python工程师标准>>> iOS精选源码 iOS高仿微信完整项目源码 Khala: Swift 编写的iOS/macOS 路由框架 微信左滑删除效果的实 ...
- iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码
iOS精选源码 iOS高仿微信完整项目源码 Khala: Swift 编写的iOS/macOS 路由框架 微信左滑删除效果的实现与TableViewCell的常用样式介绍 实现阴影圆角并存,渐变色背景 ...
- iOS高仿微信完整源码,网易爱玩APP源码等
iOS精选源码 iOS一种弹出视图效果带动画 一个快速便捷.无侵入.可扩展的动画弹框库 高仿Elk - 旅行货币转换器 iOS内分享的界面.功能一体化解决方案 使用Olami sdk实现一个语音查询股 ...
- iOS 高仿爱鲜蜂APP
//联系人:石虎 QQ: 1224614774昵称:嗡嘛呢叭咪哄 iOS高仿爱鲜蜂 前言 2016年匆匆的就过去了,又老了一岁,这一年起起伏伏,有笑声也有眼泪,感谢陪伴在我身边的人. 关于项目(代码 ...
- iOS 高仿微信相机拍摄和编辑
效果描述: 1.自定义相机 拍摄视频和照片 2.切换前后摄像头.调整焦距/设置聚焦点.横屏拍摄 3.视频编辑:涂鸦.贴图.文字水印.视频裁剪 .添加背景音乐 4 .图片编辑:涂鸦.贴图.文字水印.马赛 ...
- iOS高仿微信、仪表盘、图片标注图片滤镜、高斯模糊、上拉加载、下拉刷新等源码
iOS精选源码 Swift-图片画框标注 Swift版的上拉加载, 下拉刷新控件(一句话集成, 超级易用) iOS tabbar上的提示框 Swift图片浏览器,经过一年多维护,已基本稳定 图片滤镜 ...
- iOS高仿微信、仪表盘、图片标注图片滤镜、高斯模糊、上拉加载、下拉刷新等源码...
iOS精选源码 Swift-图片画框标注 Swift版的上拉加载, 下拉刷新控件(一句话集成, 超级易用) iOS tabbar上的提示框 Swift图片浏览器,经过一年多维护,已基本稳定 图片滤镜 ...
- iOS高仿微信悬浮窗、忍者小猪游戏、音乐播放器、支付宝、今日头条布局滚动效果等源码...
iOS精选源码 iOS WKWebView的使用源码 模仿apple music 小播放器的交互实现 高仿微信的悬浮小窗口 iOS仿支付宝首页效果 [swift]仿微信悬浮窗 类似于今日头条,网易新闻 ...
- iOS高仿百度传课,版本号2.4.1.2
高仿百度传课iOS版,版本号:2.4.1.2 运行环境:xcode6.3 ios8.3 (再往上系统没有测试) github源码链接:https://github.com/lookingstars/ ...
- iOS高仿美团外卖店铺主页
高仿美团外卖的店铺主页(包括下拉动画效果,解决各种手势问题,并且cell有列表样式,九宫格样式,卡片样式),各种动画效果纵享丝滑,因为写的比较急,还待优化.! 解决UIScrollView嵌套UISc ...
最新文章
- 转让app后AppStore应用名称下面所有者显示问题
- python django事务transaction源码分析
- 音视频中时间戳增量计算
- Table control中列隐藏实现方法
- pve虚拟化几台服务器,PVE开启嵌套虚拟化
- python根据词性进行词频统计_如何根据词性来确定语篇中的词频?
- awesome-python(python集合框架)
- bitbake 编译错误集
- 逆水寒2021最新服务器,【图片】《逆水寒》2021年1月21日更新公告【逆水寒ol吧】_百度贴吧...
- 支付宝玉伯:从前端到体验,如何把格局做大?
- 实时控制软件第一周 汽车ABS系统软件分析
- 双色球的简单逻辑!不中五百万天理难容!
- Linux下Rootkit介绍
- BIOS设置nbsp;翻译中文图文教程(一)
- 三国群英传服务器未响应,《三国群英传2》近期服务器不稳定问题说明
- 818,你所不了解的EGF重组人表皮生长因子
- 【第二届】无锡太湖学院ICPC校队对抗赛原创 IOI D题题解
- 74HC245_键盘与8段数码管共有一个IO口_控制步进电机【Protues】
- 博客营销的价值与注意事项
- 悼念乔布斯---他的产品改变了世界,思想影响了一代人,三个故事,勉励大家