SlackTextViewController 1.9.6

SlackTextViewController 1.9.6

测试已测试
语言语言 Obj-CObjective C
许可证 MIT
发布最新发布2017年11月

Ignacio RomeroIgnacio 维护。




  • Slack Technologies, Inc.

重要声明:请更新到 >= 1.9 避免应用被拒绝的风险。更多详细信息请参阅 #361

一个包含增长文本视图和其他有用消息功能的 UIViewController 子类。旨在替代 UITableViewController 和 UICollectionViewController。

Demo Gif

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

功能列表

核心功能

附加功能

兼容性

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

安装

手动

有两种方法可以实现

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

如何使用

子类化

SLKTextViewController 设计用于子类化,就像您通常对 UITableViewControllerUICollectionViewControllerUIScrollView 所做的那样。这种模式是扩展 UIViewController 的方便方式。SlackTextViewController 在幕后管理很多事情,同时仍然能够添加自定义功能。您可以覆盖方法,并决定调用 super 并执行附加逻辑,或者不调用 super 并覆盖默认逻辑。

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

在重写 init 方法时,如果您希望使用 UITableView 版本,则调用

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

UICollectionView 版本

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

UIScrollView 版本

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

例如 UITableViewDelegateUITableViewDataSource 已经为您设置好了。您将能够调用所有需要的代理和数据源方法来自定义您的控件。

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

Storyboard

当在 Storyboard 中使用 SlackTextViewController 时,您需要重写以下两种自定义方法之一,而不是重写传统的 initWithCoder:。这种方法有助于保留与程序性方法完全相同的功能,但也限制了您的 SLKTextViewController 子类的 nib 编辑,因为子视图不是从 nib 布局(子视图仍然初始化并程序化布局)。

如果您希望使用 UITableView 版本,则调用

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

UICollectionView 版本

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

示例项目

查看示例项目,那里演示了一切。有 2 个主要示例(不同的目标)用于测试程序性和 storyboard 方法,还有一个 Swift 示例。大多数功能都为您实现了,您可以快速使用它们。

请随时贡献!

功能

自动增长文本视图

Growing

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

默认情况下,行数设置为最佳适配每个设备的尺寸

  • iPhone 4 (<=480pts): 4 行
  • iPhone 5/6 (>=568pts): 6 行
  • iPad (>=768pts): 8 行

在iPhone设备上,横屏模式下,最大行数会根据可用空间进行更改。

倒序模式

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

Objective-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(kAutoCompletionCellIdentifier) {
        cell.textLabel!.text = self.searchResult[indexPath.row]
        return cell
    }
}

自动完成

我们使用自动完成来处理很多事情:姓名、频道、表情符号等等。

Autocompletion

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

1. 注册

您必须首先注册所有想要支持的自动完成检测前缀

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

2. 处理

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

一旦检测到前缀,将调用didChangeAutoCompletionPrefix:andWord:。这是填充你的数据源和显示/隐藏自动完成视图的理想位置。因此,您必须在您的子类中重写它,以便能够执行其他任务。默认返回NO。

Objective-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 pts。您可以随时更新此值,因此视图将自动根据显示的单元格数量进行调整。

Objective-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

您可以将Markdown格式化符号注册,以便可以通过原生上下文菜单(即UIMenuController)轻松使用它们来包装文本选择。这个功能不处理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 + 回车键 -> 换行
  • 退格键 -> 退出编辑模式,或自动完成模式,或关闭键盘
  • 上箭头 & 下箭头 -> 垂直光标移动

要添加额外的键控制命令,只需通过覆盖 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中使用。