iOS開發基礎144-逐字列印效果

Mr.陳發表於2024-08-01

在AIGC類的APP中,實現那種一個字一個字、一行一行地列印出文字的效果,可以透過多種方法來實現。下面是一些實現方法,使用Swift和OC來舉例說明。

OC版

1. 基於定時器的逐字列印效果

可以使用NSTimer來逐字逐行地顯示文字。

#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,其精度和效能更高。

#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和動畫來實現更為複雜和流暢的逐字逐行列印效果。

#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來逐字逐行地顯示文字。

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,其精度和效能更高。

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和動畫來實現更為複雜和流暢的逐字逐行列印效果。

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)"
            }
        }
    }
}

相關文章