重要提示:请更新到>= 1.9
以避免应用被拒绝的风险。
更详细的信息请见#361
这是一个带有扩展文本输入视图和其他有用消息功能的UIViewController子类。旨在替代UITableViewController & UICollectionViewController。
此库用于Slack的iOS应用。它是为了满足我们的需求而构建的,但足够灵活,可以供其他希望为iOS构建优秀消息应用的人重用。
@
、#
、/
)YES
/true
,所以请注意,您可能整个单元格都可能翻转!pod "SlackTextViewController"
github "slackhq/SlackTextViewController"
有两种方式来做这件事:
Source/
文件夹复制并拖到您的项目中。Builder/SlackTextViewController.xcodeproj
中的项目以创建一个 SlackTextViewController.framework
包。您还可以将库链接到您的项目中。SLKTextViewController
旨在被子类化,就像您通常对 UITableViewController
或 UICollectionViewController
或 UIScrollView
进行操作一样。这种模式是扩展 UIViewController
的便捷方式。SlackTextViewController 在幕后管理了大量的工作,同时仍然提供了添加自定义行为的能力。您可以重写方法,并决定调用超级并执行附加逻辑,或者不调用超级并重写默认逻辑。
首先,创建一个 SLKTextViewController
的新子类。
在重写初始化方法中,如果您希望使用 UITableView
版本,请调用
[super initWithTableViewStyle:UITableViewStylePlain]
super.init(tableViewStyle: .Plain)
或 UICollectionView
版本
[super initWithCollectionViewLayout:[UICollectionViewFlowLayout new]]
super.init(collectionViewLayout: UICollectionViewFlowLayout())
或 UIScrollView
版本
[super initWithScrollView:self.myStrongScrollView]
super.init(scrollView: self.myStrongScrollView)
如 UITableViewDelegate
和 UITableViewDataSource
这样的协议已经为您设置好了。您将能够调用所需的所有代理和数据源方法以自定义控件。
调用 [super init]
将默认调用 [super initWithTableViewStyle:UITableViewStylePlain]
。
当使用带有故事板的 SlackTextViewController 时,您不需要重写传统的 initWithCoder:
,您需要重写以下两种自定义方法之一。这种方法有助于保护与程序方法相同的精确功能,但也限制了您对 SLKTextViewController
子类的 nib 编辑(因为 nib 不从 nib 布局子视图,但子视图仍然被初始化和安排程序化)。
如果希望使用 UITableView
版本,请调用
+ (UITableViewStyle)tableViewStyleForCoder:(NSCoder *)decoder
{
return UITableViewStylePlain;
}
override class func tableViewStyleForCoder(decoder: NSCoder) -> UITableViewStyle {
return .Plain
}
或 UICollectionView
版本
+ (UICollectionViewLayout *)collectionViewLayoutForCoder:(NSCoder *)decoder
{
return [UICollectionViewFlowLayout new];
}
override class func collectionViewLayoutForCoder(decoder: NSCoder) -> UICollectionViewLayout {
return UICollectionViewFlowLayout()
}
请查看示例项目,那里展示了所有示例。
有两个主要的示例(不同的目标)用于测试程序性和故事板方法,还有一个 Swift 示例。大多数功能都已为您实现,以便您可以快速开始使用它们。
请随时贡献!
当需要新行时,文本视图会自动扩展,直到达到其 maxNumberOfLines
值。您可以在 textView 中更改此属性的值。
默认情况下,行数设置为最适合每种设备尺寸
在 iPhone 设备上,在横屏方向,最大行数会更改以适应可用空间。
某些布局可能需要从下到上显示,并且新子视图是从底部插入的。要启用此功能,您必须使用 inverted
标志属性(默认值为 YES
/true
)。这将实际上反转整个 ScrollView 对象。确保将对每个子视图应用相同的转换。在 UITableView 的例子中,调整转换的最佳位置是在其数据源方法中,比如
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
cell.transform = self.tableView.transform;
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier) {
cell.transform = self.tableView.transform
}
}
我们使用自动完成处理很多事项:姓名、频道、emoji 等。
要设置您的应用程序中的自动完成,请按照以下简单步骤操作
首先,您必须注册您希望支持自动完成检测的所有前缀。
[self registerPrefixesForAutoCompletion:@[@"#"]];
self.registerPrefixesForAutoCompletion(["@", "#"])
每次向文本视图插入新字符时,都会处理并验证光标附近的最接近单词是否包含任何已注册的前缀。
一旦检测到前缀,就会调用didChangeAutoCompletionPrefix:andWord:
方法。这里是填充数据源和显示/隐藏自动完成视图的最佳位置。因此,您必须在子类中覆盖它,以执行额外的任务。默认返回NO。
- (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];
}
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
方法后自动调用。
自动完成视图的最大高度默认设置为140点。您可以随时更新此值,以便视图自动根据显示的单元格数量进行调整。
- (CGFloat)heightForAutoCompletionView
{
CGFloat cellHeight = 34.0;
return cellHeight*self.searchResult.count;
}
override func heightForAutoCompletionView() -> CGFloat {
let cellHeight:CGFloat = 34
return cellHeight * CGFloat(self.searchResult.count)
}
如果用户在tableView:didSelectRowAtIndexPath:
中选择了任何自动完成视图单元格,您必须调用acceptAutoCompletionWithString:
来提交自动完成。该方法期望一个与所选项匹配的字符串,该字符串希望被插入到文本视图中。
- (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];
}
}
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。
要启用编辑模式,您只需要调用editText:
,文本输入将切换到编辑模式,删除左右按钮,使输入栏略微向上延伸带有“接受”和“取消”按钮。这两个按钮都可以在SLKTextInputbar
实例中进行自定义。
为了捕获“接受”或“取消”事件,您必须覆盖以下方法。
- (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];
}
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
属性来判断是否处于编辑模式。
您可以使用帮助的本地上下文菜单(即UIMenuController
)来注册Markdown格式化符号,以便它们可以轻松地用于包装文本选择。此功能不理会Markdown的渲染:它的唯一目的是简化用户格式化工具。
可选地,您可以启用autoCompleteFormatting
,这样在任何悬而未决的Markdown闭合符号后面的第一个空格处双击键盘空格栏,就可以自动添加,这与添加句号的本地手势相同。句号仍然作为后备添加。
您必须先注册格式化符号,并将标题字符串分配给用于菜单控制器项。
[self.textView registerMarkdownFormattingSymbol:@"*" withTitle:@"Bold"];
self.textView.registerMarkdownFormattingSymbol("*", withTitle: "Bold")
此外,您可以使用UITextViewDelegate
方法来对特殊格式化情况进行一些行为自定义。
在以下示例中,当文本选择不是段落时,我们不在上下文菜单中显示引用格式化:
- (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];
}
在此方法的另一个实现中,我们不希望允许引用格式化的自动完成,因为它不需要闭合。
- (BOOL)textView:(SLKTextView *)textView shouldInsertSuffixForFormattingWithSymbol:(NSString *)symbol prefixRange:(NSRange)prefixRange
{
if ([symbol isEqualToString:@">"]) {
return NO;
}
return [super textView:textView shouldInsertSuffixForFormattingWithSymbol:symbol prefixRange:prefixRange];
}
可选地,您可以启用一个简单的打字指示器,它将显示在文本输入上方。它显示正在打字的人的名字,如果有2个以上,它将显示“多人正在打字”消息。
要启用打字指示器,请调用
[self.typingIndicatorView insertUsername:@"John"];
self.typingIndicatorView?.insertUsername("John")
该视图将自动在文本输入上方进行动画演示。在默认间隔6秒后,如果未再次分配相同的名字,视图将使用动画消失。
您可以通过调用
[self.typingIndicatorView removeUsername:@"John"];
self.typingIndicatorView?.removeUsername("John")
来从列表中删除名字
[self.typingIndicatorView dismissIndicator];
self.typingIndicatorView?.dismissIndicator()
默认情况下,通过平移手势关闭键盘是通过keyboardPanningEnabled
属性启用的。如果您想禁用此功能,随时可以这样做。您可以使用UIGestureRecognizerDelegate
方法扩展verticalPanGesture
行为。
有时您可能需要隐藏文本输入栏。
与UINavigationViewController
的API非常相似,只需这样做
[self setTextInputbarHidden:YES animated:YES];
self.setTextInputbarHidden(true, animated: true)
通过平移手势清除文本的功能默认启用,通过undoShakingEnabled
属性。
您可以选择重写willRequestUndo
,以实现您的UI来询问用户是否想要清理文本视图中的文本。如果没有输入文本,则不会调用此方法。
如果您没有重写willRequestUndo
,并且undoShakingEnabled
设置为YES
/true
,则会显示系统警告。
默认情况下启用了几个基本的按键命令
didPressRightButton:
,或者如果在编辑模式中,调用didCommitTextEditing:
要添加额外的按键命令,只需重写keyCommands
并将其数组添加到super
中。
- (NSArray *)keyCommands
{
NSMutableArray *commands = [NSMutableArray arrayWithArray:[super keyCommands]];
// Edit last message
[commands addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow
modifierFlags:0
action:@selector(editLastMessage:)]];
return commands;
}
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
}
还有一些用于键盘特殊检测的实用标志,例如isExternalKeyboardDetected
、isKeyboardUndocked
、typingSuggestionEnabled
和isTrackpadEnabled
(仅限iOS 9)
通过dynamicTypeEnabled
属性,动态类型默认启用。如果您想禁用此功能,可以随时这样做,但文本输入栏将根据文本视图的字体大小进行调整以最佳匹配。
我们准备了一套有用的Xcode模板,使您能够快速开始使用SlackTextViewController。
要安装它们,请打开终端并输入
sh ./SlackTextViewController/File\ Templates/install.sh
这些模板也可在Alcatraz中获取。