CHSlackTextViewController 1.10.27

CHSlackTextViewController 1.10.27

测试已测试
Lang语言 Obj-CObjective C
许可证 MIT
发布最后发布2019年2月

WooWooEng维护。




  • Slack Technologies, Inc.

SlackTextViewController

重要提示:请更新到>= 1.9以避免应用被拒绝的风险。
更详细的信息请见#361

License
Pod Version
Carthage compatible
Build Status

这是一个带有扩展文本输入视图和其他有用消息功能的UIViewController子类。旨在替代UITableViewController & UICollectionViewController。

Demo Gif

此库用于Slack的iOS应用。它是为了满足我们的需求而构建的,但足够灵活,可以供其他希望为iOS构建优秀消息应用的人重用。

功能列表

核心

  • 能够在UITableView或UICollectionView或UIScrollView中直接使用
  • 扩展文本视图,支持行数限制
  • 使用Auto Layout构建的灵活UI
  • 可定制:提供左右按钮和工具栏出口
  • 点击手势以关闭键盘
  • 支持外部键盘命令
  • 撤销/重做(与键盘命令和UIMenuController)
  • 文本追加API

其他

兼容性

  • Carthage & CocoaPods
  • Objective-C & Swift
  • iOS 7、8 & 9
  • iPhone & iPad
  • Storyboard
  • UIPopOverController & UITabBarController
  • 容器视图控制器
  • 自动旋转
  • iPad多任务处理(仅iOS 9)
  • 本地化

安装

使用CocoaPods
pod "SlackTextViewController"
使用Carthage
github "slackhq/SlackTextViewController"
手动

有两种方式来做这件事:

  • Source/文件夹复制并拖到您的项目中。
  • 或者编译位于 Builder/SlackTextViewController.xcodeproj 中的项目以创建一个 SlackTextViewController.framework 包。您还可以将库链接到您的项目中

使用方法

子类化

SLKTextViewController 旨在被子类化,就像您通常对 UITableViewControllerUICollectionViewControllerUIScrollView 进行操作一样。这种模式是扩展 UIViewController 的便捷方式。SlackTextViewController 在幕后管理了大量的工作,同时仍然提供了添加自定义行为的能力。您可以重写方法,并决定调用超级并执行附加逻辑,或者不调用超级并重写默认逻辑。

首先,创建一个 SLKTextViewController 的新子类。

在重写初始化方法中,如果您希望使用 UITableView 版本,请调用

Obj-C
[super initWithTableViewStyle:UITableViewStylePlain]
Swift
super.init(tableViewStyle: .Plain)

UICollectionView 版本

Obj-C
[super initWithCollectionViewLayout:[UICollectionViewFlowLayout new]]
Swift
super.init(collectionViewLayout: UICollectionViewFlowLayout())

UIScrollView 版本

Obj-C
[super initWithScrollView:self.myStrongScrollView]
Swift
super.init(scrollView: self.myStrongScrollView)

UITableViewDelegateUITableViewDataSource 这样的协议已经为您设置好了。您将能够调用所需的所有代理和数据源方法以自定义控件。

调用 [super init] 将默认调用 [super initWithTableViewStyle:UITableViewStylePlain]

Storyboard

当使用带有故事板的 SlackTextViewController 时,您不需要重写传统的 initWithCoder:,您需要重写以下两种自定义方法之一。这种方法有助于保护与程序方法相同的精确功能,但也限制了您对 SLKTextViewController 子类的 nib 编辑(因为 nib 不从 nib 布局子视图,但子视图仍然被初始化和安排程序化)。

如果希望使用 UITableView 版本,请调用

Obj-C
+ (UITableViewStyle)tableViewStyleForCoder:(NSCoder *)decoder
{
    return UITableViewStylePlain;
}
Swift
override class func tableViewStyleForCoder(decoder: NSCoder) -> UITableViewStyle {
    return .Plain
}

UICollectionView 版本

Obj-C
+ (UICollectionViewLayout *)collectionViewLayoutForCoder:(NSCoder *)decoder
{
    return [UICollectionViewFlowLayout new];
}
Swift
override class func collectionViewLayoutForCoder(decoder: NSCoder) -> UICollectionViewLayout {
    return UICollectionViewFlowLayout()
}

示例项目

请查看示例项目,那里展示了所有示例。
有两个主要的示例(不同的目标)用于测试程序性和故事板方法,还有一个 Swift 示例。大多数功能都已为您实现,以便您可以快速开始使用它们。

请随时贡献!

功能

成长文本视图

Growing

当需要新行时,文本视图会自动扩展,直到达到其 maxNumberOfLines 值。您可以在 textView 中更改此属性的值。

默认情况下,行数设置为最适合每种设备尺寸

  • iPhone 4(≤480pts):4 行
  • iPhone 5/6(≥568pts):6 行
  • iPad(≥768pts):8 行

在 iPhone 设备上,在横屏方向,最大行数会更改以适应可用空间。

反转模式

某些布局可能需要从下到上显示,并且新子视图是从底部插入的。要启用此功能,您必须使用 inverted 标志属性(默认值为 YES/true)。这将实际上反转整个 ScrollView 对象。确保将对每个子视图应用相同的转换。在 UITableView 的例子中,调整转换的最佳位置是在其数据源方法中,比如

Obj-C
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    cell.transform = self.tableView.transform;
}
Swift
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        
    if let cell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier) {
        cell.transform = self.tableView.transform
    }
}

自动完成

我们使用自动完成处理很多事项:姓名、频道、emoji 等。

Autocompletion

要设置您的应用程序中的自动完成,请按照以下简单步骤操作

1. 注册

首先,您必须注册您希望支持自动完成检测的所有前缀。

Obj-C
[self registerPrefixesForAutoCompletion:@[@"#"]];
Swift
self.registerPrefixesForAutoCompletion(["@", "#"])

2. 处理

每次向文本视图插入新字符时,都会处理并验证光标附近的最接近单词是否包含任何已注册的前缀。

一旦检测到前缀,就会调用didChangeAutoCompletionPrefix:andWord:方法。这里是填充数据源和显示/隐藏自动完成视图的最佳位置。因此,您必须在子类中覆盖它,以执行额外的任务。默认返回NO。

Obj-C
- (void)didChangeAutoCompletionPrefix:(NSString *)prefix andWord:(NSString *)word
{
    NSArray *array = [NSArray arrayWithArray:self.channels];
    
    if ([prefix isEqualToString:@"#"] && word.length > 0) {
        self.searchResult = [array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"self BEGINSWITH[c]", word]];
    }
    
    BOOL show = (self.searchResult.count > 0);
    
    [self showAutoCompletionView:show];
}
Swift
override func didChangeAutoCompletionPrefix(prefix: String, andWord word: String) {
    
    let array: NSArray = self.channels
    
    if prefix == "#" && word.characters.count > 0 {
        self.searchResult = array.filteredArrayUsingPredicate(NSPredicate(format: "self BEGINSWITH[c] %@", word))
    }
    
    let show = (self.searchResult.count > 0)
    
    self.showAutoCompletionView(show)
}

自动完成视图是UITableView的一个实例,所以您需要使用UITableViewDataSource来填充其单元格。您有完全的自由来自定义单元格。

您不需要自己调用reloadData,因为它将在调用showAutoCompletionView方法后自动调用。

3. 布局

自动完成视图的最大高度默认设置为140点。您可以随时更新此值,以便视图自动根据显示的单元格数量进行调整。

Obj-C
- (CGFloat)heightForAutoCompletionView
{
    CGFloat cellHeight = 34.0;
    return cellHeight*self.searchResult.count;
}
Swift
override func heightForAutoCompletionView() -> CGFloat {
    let cellHeight:CGFloat = 34
    return cellHeight * CGFloat(self.searchResult.count)
}

4. 确认

如果用户在tableView:didSelectRowAtIndexPath:中选择了任何自动完成视图单元格,您必须调用acceptAutoCompletionWithString:来提交自动完成。该方法期望一个与所选项匹配的字符串,该字符串希望被插入到文本视图中。

Obj-C
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([tableView isEqual:self.autoCompletionView]) {
        
        NSMutableString *item = [self.searchResult[indexPath.row] mutableCopy];
        [item appendString:@" "]; // Adding a space helps dismissing the auto-completion view
        
        [self acceptAutoCompletionWithString:item keepPrefix:YES];
    }
}
Swift
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        
    if tableView.isEqual(tableView) {
        var item = self.searchResult[indexPath.row]
        item += " "  // Adding a space helps dismissing the auto-completion view
            
        self.acceptAutoCompletionWithString(item)
    }
}

自动完成视图将自动消失,并将在文本视图中插入所选的字符串,替换检测到的前缀和单词。

您始终可以调用cancelAutoCompletion来退出自动完成模式并刷新UI。

编辑模式

Edit Mode

要启用编辑模式,您只需要调用editText:,文本输入将切换到编辑模式,删除左右按钮,使输入栏略微向上延伸带有“接受”和“取消”按钮。这两个按钮都可以在SLKTextInputbar实例中进行自定义。

为了捕获“接受”或“取消”事件,您必须覆盖以下方法。

Obj-C
- (void)didCommitTextEditing:(id)sender
{
    NSString *message = [self.textView.text copy];
    
    [self.messages removeObjectAtIndex:0];
    [self.messages insertObject:message atIndex:0];
    [self.tableView reloadData];
    
    [super didCommitTextEditing:sender];
}

- (void)didCancelTextEditing:(id)sender
{
    [super didCancelTextEditing:sender];
}
Swift
override func didCommitTextEditing(sender: AnyObject) {
    
    let message:String = self.textView.text
    
    self.messages.removeAtIndex(0)
    self.messages.insert(message, atIndex: 0)
    
    self.tableView!.reloadData()
    
    super.didCommitTextEditing(sender)
}
 
override func didCancelTextEditing(sender: AnyObject) {
        
    super.didCancelTextEditing(sender)
}

请注意,您必须在某个位置调用super,以便文本输入退出编辑模式,重新调整布局并清除文本视图。
使用editing属性来判断是否处于编辑模式。

Markdown格式化

Markdown Formatting

您可以使用帮助的本地上下文菜单(即UIMenuController)来注册Markdown格式化符号,以便它们可以轻松地用于包装文本选择。此功能不理会Markdown的渲染:它的唯一目的是简化用户格式化工具。
可选地,您可以启用autoCompleteFormatting,这样在任何悬而未决的Markdown闭合符号后面的第一个空格处双击键盘空格栏,就可以自动添加,这与添加句号的本地手势相同。句号仍然作为后备添加。

Markdown Formatting Animated

1. 注册

您必须先注册格式化符号,并将标题字符串分配给用于菜单控制器项。

Obj-C
[self.textView registerMarkdownFormattingSymbol:@"*" withTitle:@"Bold"];
Swift
self.textView.registerMarkdownFormattingSymbol("*", withTitle: "Bold")

2. 自定义

此外,您可以使用UITextViewDelegate方法来对特殊格式化情况进行一些行为自定义。
在以下示例中,当文本选择不是段落时,我们不在上下文菜单中显示引用格式化:

Obj-C
- (BOOL)textView:(SLKTextView *)textView shouldOfferFormattingForSymbol:(NSString *)symbol
{
    if ([symbol isEqualToString:@">"]) {
        
        NSRange selection = textView.selectedRange;
        
        // The Quote formatting only applies new paragraphs
        if (selection.location == 0 && selection.length > 0) {
            return YES;
        }
        
        // or older paragraphs too
        NSString *prevString = [textView.text substringWithRange:NSMakeRange(selection.location-1, 1)];
        
        if ([[NSCharacterSet newlineCharacterSet] characterIsMember:[prevString characterAtIndex:0]]) {
            return YES;
        }

        return NO;
    }
    
    return [super textView:textView shouldOfferFormattingForSymbol:symbol];
}

在此方法的另一个实现中,我们不希望允许引用格式化的自动完成,因为它不需要闭合。

Obj-C
- (BOOL)textView:(SLKTextView *)textView shouldInsertSuffixForFormattingWithSymbol:(NSString *)symbol prefixRange:(NSRange)prefixRange
{
    if ([symbol isEqualToString:@">"]) {
        return NO;
    }
    
    return [super textView:textView shouldInsertSuffixForFormattingWithSymbol:symbol prefixRange:prefixRange];
}

打字指示器

Typing Indicator

可选地,您可以启用一个简单的打字指示器,它将显示在文本输入上方。它显示正在打字的人的名字,如果有2个以上,它将显示“多人正在打字”消息。

要启用打字指示器,请调用

Obj-C
[self.typingIndicatorView insertUsername:@"John"];
Swift
self.typingIndicatorView?.insertUsername("John")

该视图将自动在文本输入上方进行动画演示。在默认间隔6秒后,如果未再次分配相同的名字,视图将使用动画消失。

您可以通过调用

Obj-C
[self.typingIndicatorView removeUsername:@"John"];
Swift
self.typingIndicatorView?.removeUsername("John")

来从列表中删除名字

Obj-C
[self.typingIndicatorView dismissIndicator];
Swift
self.typingIndicatorView?.dismissIndicator()

来关闭它

默认情况下,通过平移手势关闭键盘是通过keyboardPanningEnabled属性启用的。如果您想禁用此功能,随时可以这样做。您可以使用UIGestureRecognizerDelegate方法扩展verticalPanGesture行为。

隐藏的TextInputbar

有时您可能需要隐藏文本输入栏。
UINavigationViewController的API非常相似,只需这样做

Obj-C
[self setTextInputbarHidden:YES animated:YES];
Swift
self.setTextInputbarHidden(true, animated: true)

摇晃手势

Shake Gesture

通过平移手势清除文本的功能默认启用,通过undoShakingEnabled属性。

您可以选择重写willRequestUndo,以实现您的UI来询问用户是否想要清理文本视图中的文本。如果没有输入文本,则不会调用此方法。

如果您没有重写willRequestUndo,并且undoShakingEnabled设置为YES/true,则会显示系统警告。

外部键盘

默认情况下启用了几个基本的按键命令

  • cmd + z -> 撤销
  • shift + cmd + z -> 重做
  • 回车键 -> 调用didPressRightButton:,或者如果在编辑模式中,调用didCommitTextEditing:
  • shift/cmd + 回车键 -> 换行
  • esc键 -> 退出编辑模式,自动补全模式,或关闭键盘
  • 上箭头 & 下箭头 -> 垂直光标移动

要添加额外的按键命令,只需重写keyCommands并将其数组添加到super中。

Obj-C
- (NSArray *)keyCommands
{
    NSMutableArray *commands = [NSMutableArray arrayWithArray:[super keyCommands]];
    
    // Edit last message
    [commands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow
                                           modifierFlags:0
                                                   action:@selector(editLastMessage:)]];
    
    return commands;
}
Swift
override var keyCommands: [UIKeyCommand]? {
        
    var commands = super.keyCommands
        
    // Edit last message
    let command = UIKeyCommand(input: UIKeyInputUpArrow, modifierFlags: .Command, action: "editLastMessage:")
    commands?.append(command)
        
    return commands
}

还有一些用于键盘特殊检测的实用标志,例如isExternalKeyboardDetectedisKeyboardUndockedtypingSuggestionEnabledisTrackpadEnabled(仅限iOS 9)

动态类型

通过dynamicTypeEnabled属性,动态类型默认启用。如果您想禁用此功能,可以随时这样做,但文本输入栏将根据文本视图的字体大小进行调整以最佳匹配。

Dynamic-Type

Xcode模板

Template

我们准备了一套有用的Xcode模板,使您能够快速开始使用SlackTextViewController。

要安装它们,请打开终端并输入

sh ./SlackTextViewController/File\ Templates/install.sh

这些模板也可在Alcatraz中获取。