diff --git a/Pica.xcodeproj/project.pbxproj b/Pica.xcodeproj/project.pbxproj index 0650f30..72244e2 100644 --- a/Pica.xcodeproj/project.pbxproj +++ b/Pica.xcodeproj/project.pbxproj @@ -88,6 +88,7 @@ 33828E4025E6577C00967957 /* PCRegistRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 33828E3F25E6577C00967957 /* PCRegistRequest.m */; }; 33828E4425E65CB600967957 /* PCRegistController.m in Sources */ = {isa = PBXBuildFile; fileRef = 33828E4325E65CB600967957 /* PCRegistController.m */; }; 3392771F27229819000021A3 /* PCChatSettingController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3392771E27229819000021A3 /* PCChatSettingController.m */; }; + 339E4A7C2806A8C3009730CB /* PCSpeechSynthesizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 339E4A7B2806A8C3009730CB /* PCSpeechSynthesizer.m */; }; 33A56880278D26A30059E076 /* PCImageSizeCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 33A5687F278D26A30059E076 /* PCImageSizeCache.m */; }; 33AB826E2679E0BA00A2FD3F /* PCImageMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 33AB826D2679E0BA00A2FD3F /* PCImageMessageCell.m */; }; 33AB8276267B377300A2FD3F /* PCVoiceMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 33AB8275267B377300A2FD3F /* PCVoiceMessageCell.m */; }; @@ -314,6 +315,8 @@ 33828E4325E65CB600967957 /* PCRegistController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PCRegistController.m; sourceTree = ""; }; 3392771D27229819000021A3 /* PCChatSettingController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PCChatSettingController.h; sourceTree = ""; }; 3392771E27229819000021A3 /* PCChatSettingController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PCChatSettingController.m; sourceTree = ""; }; + 339E4A7A2806A8C3009730CB /* PCSpeechSynthesizer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PCSpeechSynthesizer.h; sourceTree = ""; }; + 339E4A7B2806A8C3009730CB /* PCSpeechSynthesizer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PCSpeechSynthesizer.m; sourceTree = ""; }; 33A5687E278D26A30059E076 /* PCImageSizeCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PCImageSizeCache.h; sourceTree = ""; }; 33A5687F278D26A30059E076 /* PCImageSizeCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PCImageSizeCache.m; sourceTree = ""; }; 33AB826C2679E0BA00A2FD3F /* PCImageMessageCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PCImageMessageCell.h; sourceTree = ""; }; @@ -472,6 +475,8 @@ 33A5687F278D26A30059E076 /* PCImageSizeCache.m */, 33D4110F279AA9200017957E /* PCLocalAuthentication.h */, 33D41110279AA9200017957E /* PCLocalAuthentication.m */, + 339E4A7A2806A8C3009730CB /* PCSpeechSynthesizer.h */, + 339E4A7B2806A8C3009730CB /* PCSpeechSynthesizer.m */, ); path = Class; sourceTree = ""; @@ -1342,6 +1347,7 @@ 334EAD89255A545200B0231C /* PCUser.m in Sources */, 334EAD85255A522700B0231C /* PCComicDetailController.m in Sources */, 3329FE53265B90C40054F39B /* PCKnightRankCell.m in Sources */, + 339E4A7C2806A8C3009730CB /* PCSpeechSynthesizer.m in Sources */, 3306A455266A1C8C00FC397A /* PCCommentChildRequest.m in Sources */, 334EAD6F25593E8800B0231C /* PCComicListCell.m in Sources */, 33828E4025E6577C00967957 /* PCRegistRequest.m in Sources */, @@ -1515,7 +1521,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 1.2.5; + MARKETING_VERSION = 1.3.0; PRODUCT_BUNDLE_IDENTIFIER = com.yuecheng.pica; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1541,7 +1547,7 @@ "@executable_path/Frameworks", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; - MARKETING_VERSION = 1.2.5; + MARKETING_VERSION = 1.3.0; PRODUCT_BUNDLE_IDENTIFIER = com.yuecheng.pica; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/Pica/Chat/Controller/PCChatSettingController.m b/Pica/Chat/Controller/PCChatSettingController.m index 95d23a5..21cb529 100644 --- a/Pica/Chat/Controller/PCChatSettingController.m +++ b/Pica/Chat/Controller/PCChatSettingController.m @@ -11,6 +11,7 @@ #import "PCColorPickerViewController.h" #import "PCAvatarDecorateController.h" #import "PCUser.h" +#import "PCSpeechSynthesizer.h" @interface PCChatSetting : NSObject @@ -59,6 +60,15 @@ + (PCChatSetting *)chatLV { return setting; } ++ (PCChatSetting *)chatSpeak { + PCChatSetting *setting = [[PCChatSetting alloc] init]; + setting.title = @"聊天语音播放"; + setting.value = [kPCUserDefaults stringForKey:PC_CHAT_SPEAK]; + setting.on = [kPCUserDefaults boolForKey:PC_CHAT_SPEAK_ON]; + setting.key = PC_CHAT_SPEAK; + return setting; +} + @end @interface PCChatSettingController () @@ -203,6 +213,9 @@ - (void)switchAction:(UISwitch *)sender { PCChatSetting *setting = self.dataSource[indexPath.section]; setting.on = !setting.on; [kPCUserDefaults setBool:setting.on forKey:[NSString stringWithFormat:@"%@_ON", setting.key]]; + if ([setting.key isEqualToString:PC_CHAT_SPEAK] && !setting.on) { + [[PCSpeechSynthesizer sharedInstance] stopSpeak]; + } [self.tableView reloadData]; } @@ -212,7 +225,8 @@ - (void)switchAction:(UISwitch *)sender { _dataSource = @[[PCChatSetting chatLV], [PCChatSetting chatTitle], [PCChatSetting chatTextColor], - [PCChatSetting chatAvatarDecorate]]; + [PCChatSetting chatAvatarDecorate], + [PCChatSetting chatSpeak]]; } return _dataSource; } diff --git a/Pica/Chat/Controller/PCChatViewController.m b/Pica/Chat/Controller/PCChatViewController.m index 46c5457..ede3375 100644 --- a/Pica/Chat/Controller/PCChatViewController.m +++ b/Pica/Chat/Controller/PCChatViewController.m @@ -16,7 +16,9 @@ #import "PCUser.h" #import "PCChatMessage.h" #import "UIImage+PCAdd.h" +#import "PCSpeechSynthesizer.h" #import "PCOrderListController.h" +#import "PCChatSettingController.h" #import static CGFloat const kChatBarTextViewBottomOffset = 10; @@ -92,7 +94,7 @@ - (void)initSubviews { - (void)setupNavigationItems { [super setupNavigationItems]; self.titleView.style = QMUINavigationTitleViewStyleSubTitleVertical; - self.navigationItem.rightBarButtonItem = [UIBarButtonItem qmui_itemWithTitle:@"指令" target:self action:@selector(orderAction:)]; + self.navigationItem.rightBarButtonItems = @[[UIBarButtonItem qmui_itemWithTitle:@"指令" target:self action:@selector(orderAction:)], [UIBarButtonItem qmui_itemWithTitle:@"设置" target:self action:@selector(settingAction:)]]; } - (void)viewDidLayoutSubviews { @@ -185,6 +187,11 @@ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPa } #pragma mark - Action +- (void)settingAction:(id)sender { + PCChatSettingController *setting = [[PCChatSettingController alloc] initWithStyle:UITableViewStyleGrouped]; + [self.navigationController pushViewController:setting animated:YES]; +} + - (void)orderAction:(id)sender { PCOrderListController *orderList = [[PCOrderListController alloc] init]; @weakify(self) @@ -469,6 +476,25 @@ - (PCUser *)atUserWithId:(NSString *)userId return user; } +- (void)broadcastMessage:(PCChatMessage *)message { + NSString *string = nil; + + if (message.messageType == PCChatMessageTypeDefault) { + if (message.reply_name.length) { + string = [NSString stringWithFormat:@"%@回复%@说:%@", message.name, message.reply_name, message.message]; + } else { + string = [NSString stringWithFormat:@"%@说:%@", message.name, message.message]; + } + } else if (message.messageType == PCChatMessageTypeImage) { + string = [NSString stringWithFormat:@"%@发了一张图片", message.name]; + } else if (message.messageType == PCChatMessageTypeAudio) { + string = [NSString stringWithFormat:@"%@发了一条语音", message.name]; + } + if (string) { + [[PCSpeechSynthesizer sharedInstance] speakText:string]; + } +} + #pragma mark - Record - (void)startRecord { AVAudioSession *session = [AVAudioSession sharedInstance]; @@ -681,6 +707,9 @@ - (PCChatManager *)manager { if (message) { if (message.messageType == PCChatMessageTypeDefault || message.messageType == PCChatMessageTypeImage || message.messageType == PCChatMessageTypeAudio) { + if ([kPCUserDefaults boolForKey:PC_CHAT_SPEAK_ON]) { + [self broadcastMessage:message]; + } [self insertMessage:message scrollToBottom:YES]; } else if (message.messageType == PCChatMessageTypeConnectionCount && self.navigationController.topViewController == self) { self.titleView.subtitle = [NSString stringWithFormat:@"在线人数:%@", @(message.connections)]; diff --git a/Pica/Common/Class/PCSpeechSynthesizer.h b/Pica/Common/Class/PCSpeechSynthesizer.h new file mode 100644 index 0000000..df30979 --- /dev/null +++ b/Pica/Common/Class/PCSpeechSynthesizer.h @@ -0,0 +1,22 @@ +// +// PCSpeechSynthesizer.h +// Pica +// +// Created by Fancy on 2022/4/13. +// Copyright © 2022 fancy. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PCSpeechSynthesizer : NSObject + ++ (instancetype)sharedInstance; + +- (void)speakText:(NSString *)string; +- (void)stopSpeak; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Pica/Common/Class/PCSpeechSynthesizer.m b/Pica/Common/Class/PCSpeechSynthesizer.m new file mode 100644 index 0000000..806037f --- /dev/null +++ b/Pica/Common/Class/PCSpeechSynthesizer.m @@ -0,0 +1,61 @@ +// +// PCSpeechSynthesizer.m +// Pica +// +// Created by Fancy on 2022/4/13. +// Copyright © 2022 fancy. All rights reserved. +// + +#import "PCSpeechSynthesizer.h" +#import + +@interface PCSpeechSynthesizer () + +@property (nonatomic, strong) AVSpeechSynthesizer *synthesizer; + +@end + +@implementation PCSpeechSynthesizer + ++ (instancetype)sharedInstance { + static dispatch_once_t onceToken; + static PCSpeechSynthesizer *instance = nil; + dispatch_once(&onceToken, ^{ + instance = [[super allocWithZone:NULL] init]; + }); + return instance; +} + ++ (id)allocWithZone:(struct _NSZone *)zone { + return [self sharedInstance]; +} + +- (void)speakText:(NSString *)string { +// AVSpeechUtteranceMinimumSpeechRate 0 +// AVSpeechUtteranceMaximumSpeechRate 1 +// AVSpeechUtteranceDefaultSpeechRate 0.5 + AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:string]; + utterance.rate = 0.6; + utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"zh-CN"]; + [self.synthesizer speakUtterance:utterance]; +} + +- (void)stopSpeak { + [self.synthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate]; +} + +#pragma mark - AVSpeechSynthesizerDelegate +- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance;{ + +} + +#pragma mark - Get +- (AVSpeechSynthesizer *)synthesizer{ + if (!_synthesizer) { + _synthesizer = [[AVSpeechSynthesizer alloc] init]; + _synthesizer.delegate = self; + } + return _synthesizer; +} + +@end diff --git a/Pica/Header/PCLocalKeyHeader.h b/Pica/Header/PCLocalKeyHeader.h index 4a2d5a4..7ea9766 100644 --- a/Pica/Header/PCLocalKeyHeader.h +++ b/Pica/Header/PCLocalKeyHeader.h @@ -30,6 +30,8 @@ #define PC_CHAT_TITLE_ON @"PC_CHAT_TITLE_ON" #define PC_CHAT_LV @"PC_CHAT_LV" #define PC_CHAT_LV_ON @"PC_CHAT_LV_ON" +#define PC_CHAT_SPEAK @"PC_CHAT_SPEAK" +#define PC_CHAT_SPEAK_ON @"PC_CHAT_SPEAK_ON" #define PC_NSFW_INVALID_URL @"PC_NSFW_INVALID_URL" #define PC_NSFW_ON @"PC_NSFW_ON" #define PC_UMENG_APP_KEY @"61fce5dfe0f9bb492bf30a2c" diff --git a/README.md b/README.md index c4deedb..d9041b3 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,34 @@ # Pica 仅供学习&个人使用 --- 一个**PicAcg**客户端 -API来自 [picacomic-api](https://github.com/czp3009/picacomic-api) +API来自 [picacomic-api](https://github.com/czp3009/picacomic-api) + +#### 软件截图 +[![md.jpg](https://z3.ax1x.com/2021/06/28/RNHakQ.md.jpg)](https://imgtu.com/i/RNHakQ) + +#### 软件功能 +* [x] 登录注册 +* [x] 排行榜 +* [x] 随机本子 +* [x] 分类 +* [x] 搜索 +* [x] 评论列表 +* [x] 上传头像 +* [x] 修改slogan +* [x] 修改密码 +* [x] 我收藏的本子 +* [x] 我的评论 +* [x] 评论本子&回复评论 +* [x] 浏览历史 +* [x] 留言板 +* [x] 聊天室接收发送图片,文字,声音 +* [x] 游戏区 +* [ ] 下载本子 #### 更新日志 +**v1.3.0** +* 聊天室增加消息语音播放 + **v1.2.5** * tag/translate/creator/author 搜索漫画列表页数bug fix @@ -107,27 +132,6 @@ API来自 [picacomic-api](https://github.com/czp3009/picacomic-api) * 我的模块 -#### 软件截图 -[![md.jpg](https://z3.ax1x.com/2021/06/28/RNHakQ.md.jpg)](https://imgtu.com/i/RNHakQ) - -#### 软件功能 -* [x] 登录注册 -* [x] 排行榜 -* [x] 随机本子 -* [x] 分类 -* [x] 搜索 -* [x] 评论列表 -* [x] 上传头像 -* [x] 修改slogan -* [x] 修改密码 -* [x] 我收藏的本子 -* [x] 我的评论 -* [x] 评论本子&回复评论 -* [x] 浏览历史 -* [x] 留言板 -* [x] 聊天室接收发送图片,文字,声音 -* [x] 游戏区 -* [ ] 下载本子 #### 免责声明 本项目仅供学习&个人使用,版权归原漫画作者及发行商所有。