如何在Swift应用程序中创建一个最小的控制台(UILabel,UITextView,滚动,截断头?)

时间:2017-09-15 12:53:10

标签: swift uilabel uitextview

我想在我的应用程序中创建一个控制台,以便在屏幕上显示活动或信息。它将以一个简单的字符串开头,"等待新消息......"当不同的事情发生时,新的消息将被添加到该字符串。当控制台填满时,该字段将在每次添加时滚动,以便用户始终在控制台底部看到最新消息,旧消息从顶部视图中消失。

有" Truncate Head"和多行UILabel?我首先尝试使用UILabel执行此操作,但我找不到始终查看字符串END的方法。截断头实际上是逐行工作的,所以它会显示字符串的前五行,然后显示最后一行的尾部。我尝试了各种对齐和包装设置,但没有任何效果...我错过了什么?有没有办法让UILabel始终显示字符串的结尾,让内容从顶部消失?

每次剪切字符串以适应?也许我可以将字符串剪切为最后一千个字符或类似字符?但我不知道UILabel会有多大(在不同的屏幕上)......即使我这样做了,字体就是它们的样子,我怀疑我能确切地知道我应该修剪字符串的字符数至。我无法将其修剪到给定量的空间并获得我的UILabel中的空间量,是吗?

或者,我可以使用UITextView和Scroll 也许这就是我必须要做的。我可以获取文本视图文本的全部值并将新字符串添加到它并将其放回到UITextView中,然后使用NSMakeRange和.scrollRangeToBottom滚动到底部。

    func updateConsole(switchType: String) {

         //unwind the console's text

         if let tempString = consoleZe.text {
             currentText = tempString
         }

         consoleZe.text = currentText + "A new message here! Something clever taken from \(switchType).\n"

         //Scroll to the bottom

         let bottom = NSMakeRange(consoleZe.text.characters.count - 1, 1)
         consoleZe.scrollRangeToVisible(bottom)
}

对于我的小型更新控制台来说,这似乎有很多工作要做。我不在乎滚动以查看过去的值。我甚至更喜欢控制台没有滚动...所以抓取,添加,粘贴,获取底部然后滚动似乎是很多额外的,不必要的行李。

欢迎使用UILabel或UITextView或任何其他方式实现最小控制台的所有想法,谢谢!

1 个答案:

答案 0 :(得分:1)

我使用tableview和“ConsoleBuffer”类作为数据源实现了“Console View Controller”。 tableview很好地与消息记录控制台的逐行导向性质相对应 - 并且使自动滚动变得容易。

ConsoleBuffer是一个单例类,它将控制台消息保存在一个简单的字符串数组中,并附加了一些辅助函数。请参阅下面的完整ConsoleBuffer实施:

class ConsoleBuffer {
    struct Prefs {
        static let defaultLines = 100
        static let maxLines = 1000
    }
    static let shared = ConsoleBuffer()

    private var buffer = [String]() {
        didSet {
            if buffer.count > lines {
                buffer.removeFirst(buffer.count - lines)
            }
            tableView?.reloadData()
            NSAnimationContext.runAnimationGroup({ (context) in
                if let tableView = self.tableView {
                    if let scrollView = tableView.enclosingScrollView {
                        let range = tableView.rows(in: scrollView.contentView.visibleRect)
                        let lastRow = range.location + range.length
                        if lastRow == oldValue.count - 1 {
                            context.allowsImplicitAnimation = true
                            tableView.scrollRowToVisible(buffer.count - 1)
                        }
                    }
                }
            }, completionHandler: nil)

        }
    }

    var lines = ConsoleBuffer.Prefs.defaultLines {
        didSet {
            if lines > ConsoleBuffer.Prefs.maxLines {
                lines = ConsoleBuffer.Prefs.maxLines
            }
        }
    }

    var count: Int {
        get {
            return buffer.count
        }
    }

    var tableView: NSTableView?

    private init() { }

    func line(_ n: Int) -> String {
        if n >= 0 && n < buffer.count {
            return buffer[n]
        } else {
            return ""
        }
    }

    func add(_ line: String) {
        let dateStampedLine = "\(Date()) \(line)"
        buffer.append(dateStampedLine)
    }

    func clear() {
        buffer.removeAll()
    }    
}

这两个陈述使ConsoleBuffer成为单身人士:

static let shared = ConsoleBuffer()
private init() { }

拥有单例可以轻松地在项目的任何位置添加新的控制台行,而无需引用类的实例。使init私有化会阻止任何人调用ConsoleBuffer() - 而是强制您使用其单例实例:ConsoleBuffer.shared

控制台行字符串保存在buffer数组中,该数组是私有的,以保持其实现隐藏。向此数组添加新行时,tableview会平滑地滚动到添加的最后一行,但前提是显示最后一行。否则滚动位置保持不变。

现在,数据源易于实现:

func numberOfRows(in tableView: NSTableView) -> Int {
    return ConsoleBuffer.shared.count
}

func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
    let cell = tableView.make(withIdentifier: "ConsoleCell", owner: self) as? NSTableCellView
    cell?.textField?.stringValue = ConsoleBuffer.shared.line(row)
    return cell
}

在tableview控制器的viewDidLoad函数中,您需要将tableView ConsoleBuffer属性设置为使用的tableview。此外,这是设置要存储在缓冲区数组中的所需最大行数的位置:

ConsoleBuffer.shared.tableView = tableView
ConsoleBuffer.shared.lines = 500

现在您可以像这样在控制台中添加新行:

ConsoleBuffer.shared.add("console message")

希望这能让你走向正确的方向。