iOS开发基础144-逐字打印效果

在AIGC类的APP中,实现那种一个字一个字、一行一行地打印出文字的效果,可以通过多种方法来实现。下面是一些实现方法,使用Swift和OC来举例说明。

OC版

1. 基于定时器的逐字打印效果

可以使用NSTimer来逐字逐行地显示文字。

objc 复制代码
#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) UITextView *textView;
@property (nonatomic, strong) NSString *content;
@property (nonatomic, assign) NSInteger currentIndex;
@property (nonatomic, strong) NSTimer *timer;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.textView = [[UITextView alloc] initWithFrame:self.view.bounds];
    self.textView.font = [UIFont systemFontOfSize:18];
    self.textView.editable = NO;
    self.textView.scrollEnabled = YES;
    [self.view addSubview:self.textView];

    self.content = @"这是需要逐字逐行打印的文字内容。\n让我们来实现它。";
    self.currentIndex = 0;

    [self startPrinting];
}

- (void)startPrinting {
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(printNextCharacter) userInfo:nil repeats:YES];
}

- (void)printNextCharacter {
    if (self.currentIndex >= self.content.length) {
        [self.timer invalidate];
        self.timer = nil;
        return;
    }

    NSRange range = NSMakeRange(self.currentIndex, 1);
    NSString *nextCharacter = [self.content substringWithRange:range];
    self.textView.text = [self.textView.text stringByAppendingString:nextCharacter];
    
    self.currentIndex += 1;
}

@end

2. 使用CADisplayLink来实现高精度逐字打印

CADisplayLink可以在屏幕刷新时调用指定的方法,相较于NSTimer,其精度和性能更高。

objc 复制代码
#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) UITextView *textView;
@property (nonatomic, strong) NSString *content;
@property (nonatomic, assign) NSInteger currentIndex;
@property (nonatomic, strong) CADisplayLink *displayLink;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.textView = [[UITextView alloc] initWithFrame:self.view.bounds];
    self.textView.font = [UIFont systemFontOfSize:18];
    self.textView.editable = NO;
    self.textView.scrollEnabled = YES;
    [self.view addSubview:self.textView];
    
    self.content = @"这是需要逐字逐行打印的文字内容。\n让我们来实现它。";
    self.currentIndex = 0;

    [self startPrinting];
}

- (void)startPrinting {
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(printNextCharacter)];
    self.displayLink.preferredFramesPerSecond = 10; // 控制打印速度
    [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}

- (void)printNextCharacter {
    if (self.currentIndex >= self.content.length) {
        [self.displayLink invalidate];
        self.displayLink = nil;
        return;
    }

    NSRange range = NSMakeRange(self.currentIndex, 1);
    NSString *nextCharacter = [self.content substringWithRange:range];
    self.textView.text = [self.textView.text stringByAppendingString:nextCharacter];
    
    self.currentIndex += 1;
}

@end

3. CATextLayer + Animation

还可以使用CATextLayer和动画来实现更为复杂和流畅的逐字逐行打印效果。

objc 复制代码
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>

@interface ViewController ()

@property (nonatomic, strong) CATextLayer *textLayer;
@property (nonatomic, strong) NSString *content;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.textLayer = [CATextLayer layer];
    self.textLayer.frame = self.view.bounds;
    self.textLayer.fontSize = 18;
    self.textLayer.alignmentMode = kCAAlignmentLeft;
    self.textLayer.contentsScale = [UIScreen mainScreen].scale;
    self.textLayer.wrapped = YES;
    [self.view.layer addSublayer:self.textLayer];

    self.content = @"这是需要逐字逐行打印的文字内容。\n让我们来实现它。";

    [self startPrinting];
}

- (void)startPrinting {
    self.textLayer.string = @"";
    
    for (NSInteger index = 0; index < self.content.length; index++) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(index * 0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSString *nextCharacter = [self.content substringWithRange:NSMakeRange(index, 1)];
            self.textLayer.string = [self.textLayer.string stringByAppendingString:nextCharacter];
        });
    }
}

@end

Swift版

1. 基于定时器的逐字打印效果

可以使用Timer来逐字逐行地显示文字。

swift 复制代码
import UIKit

class ViewController: UIViewController {
    private let textView = UITextView()
    private let content = "这是需要逐字逐行打印的文字内容。\n让我们来实现它。"
    private var currentIndex = 0
    private var timer: Timer?

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(textView)
        textView.frame = view.bounds
        textView.font = UIFont.systemFont(ofSize: 18)
        textView.isEditable = false
        textView.isScrollEnabled = true
        startPrinting()
    }

    private func startPrinting() {
        timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(printNextCharacter), userInfo: nil, repeats: true)
    }

    @objc private func printNextCharacter() {
        guard currentIndex < content.count else {
            timer?.invalidate()
            timer = nil
            return
        }
        
        let nextIndex = content.index(content.startIndex, offsetBy: currentIndex)
        textView.text.append(content[nextIndex])
        currentIndex += 1
    }
}

2. 使用CADisplayLink来实现高精度逐字打印

CADisplayLink可以在屏幕刷新时调用指定的方法,相较于Timer,其精度和性能更高。

swift 复制代码
import UIKit

class ViewController: UIViewController {
    private let textView = UITextView()
    private let content = "这是需要逐字逐行打印的文字内容。\n让我们来实现它。"
    private var currentIndex = 0
    private var displayLink: CADisplayLink?

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(textView)
        textView.frame = view.bounds
        textView.font = UIFont.systemFont(ofSize: 18)
        textView.isEditable = false
        textView.isScrollEnabled = true
        startPrinting()
    }

    private func startPrinting() {
        displayLink = CADisplayLink(target: self, selector: #selector(printNextCharacter))
        displayLink?.preferredFramesPerSecond = 10  // 控制打印速度
        displayLink?.add(to: .main, forMode: .default)
    }

    @objc private func printNextCharacter() {
        guard currentIndex < content.count else {
            displayLink?.invalidate()
            displayLink = nil
            return
        }
        
        let nextIndex = content.index(content.startIndex, offsetBy: currentIndex)
        textView.text.append(content[nextIndex])
        currentIndex += 1
    }
}

3. CATextLayer + Animation

还可以使用CATextLayer和动画来实现更为复杂和流畅的逐字逐行打印效果。

swift 复制代码
import UIKit

class ViewController: UIViewController {
    private let textLayer = CATextLayer()
    private let content = "这是需要逐字逐行打印的文字内容。\n让我们来实现它。"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        textLayer.frame = view.bounds
        textLayer.fontSize = 18
        textLayer.alignmentMode = .left
        textLayer.contentsScale = UIScreen.main.scale
        textLayer.isWrapped = true
        view.layer.addSublayer(textLayer)
        
        startPrinting()
    }
    
    private func startPrinting() {
        textLayer.string = ""
        for (index, character) in content.enumerated() {
            DispatchQueue.main.asyncAfter(deadline: .now() + Double(index) * 0.1) {
                self.textLayer.string = "\(self.textLayer.string ?? "")\(character)"
            }
        }
    }
}