Вставить разрыв строки с помощью SKLabelNode в SpriteKit
Простой вопрос о том, как вставить разрыв строки, используя класс SKLabelNode в SpriteKit. У меня есть следующий код, но он не работает -
SKLabelNode *nerdText = [SKLabelNode labelNodeWithFontNamed:@"Times"];
NSString *st1 = @"Test break";
NSString *st2 = @"I want it to break";
NSString *test = [NSString stringWithFormat:@"%@,\r%@",st1,st2]; //Even tried \n
nerdText.text = test;
nerdText.fontSize = 11;
nerdText.fontColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
nerdText.position = CGPointMake(150.0, 250.0);
[self addChild:nerdText];
Пожалуйста, помогите мне!
Ответы
Ответ 1
Я не думаю, что вы можете, вот способ "взломать" это сделать
SKNode *nerdText = [SKNode node];
SKLabelNode *a = [SKLabelNode labelNodeWithFontNamed:@"Arial"];
a.fontSize = 16;
a.fontColor = [SKColor yellowColor];
SKLabelNode *b = [SKLabelNode labelNodeWithFontNamed:@"Arial"];
b.fontSize = 16;
b.fontColor = [SKColor yellowColor];
NSString *st1 = @"Line 1";
NSString *st2 = @"Line 2";
b.position = CGPointMake(b.position.x, b.position.y - 20);
a.text = st1;
b.text = st2;
[nerdText addChild:a];
[nerdText addChild:b];
nerdText.position = CGPointMake(150.0, 250.0);
[self addChild:nerdText];
Ответ 2
У меня была та же проблема. Я создал замену для SKLabelNode под названием DSMultilineLabelNode, которая поддерживает перенос слов, разрывы строк и т.д. Основная реализация рисует строку в графический контекст, а затем применяет ее к текстуре на SKSpriteNode.
Он доступен на GitHub по адресу:
https://github.com/downrightsimple/DSMultilineLabelNode
Ответ 3
static func multipleLineText(labelInPut: SKLabelNode) -> SKLabelNode {
let subStrings:[String] = labelInPut.text!.componentsSeparatedByString("\n")
var labelOutPut = SKLabelNode()
var subStringNumber:Int = 0
for subString in subStrings {
let labelTemp = SKLabelNode(fontNamed: labelInPut.fontName)
labelTemp.text = subString
labelTemp.fontColor = labelInPut.fontColor
labelTemp.fontSize = labelInPut.fontSize
labelTemp.position = labelInPut.position
labelTemp.horizontalAlignmentMode = labelInPut.horizontalAlignmentMode
labelTemp.verticalAlignmentMode = labelInPut.verticalAlignmentMode
let y:CGFloat = CGFloat(subStringNumber) * labelInPut.fontSize
print("y is \(y)")
if subStringNumber == 0 {
labelOutPut = labelTemp
subStringNumber++
} else {
labelTemp.position = CGPoint(x: 0, y: -y)
labelOutPut.addChild(labelTemp)
subStringNumber++
}
}
return labelOutPut
}
Ответ 4
Вот еще пять минут на твоем пути. Это не так уж плохо.
+(SKSpriteNode*)spritenodecontaininglabelsFromStringcontainingnewlines:(NSString*)text fontname:(NSString*)fontname fontcolor:(NSColor*)colorFont fontsize:(const CGFloat)SIZEFONT verticalMargin:(const CGFloat)VERTICALMARGIN emptylineheight:(const CGFloat)EMPTYLINEHEIGHT {
NSArray* strings = [text componentsSeparatedByString:@"\n"];
//DLog(@"string count: %lu", (unsigned long)strings.count);
NSColor* color = NSColor.clearColor;
#ifdef DEBUG
color = [NSColor colorWithCalibratedRed:1 green:0 blue:0 alpha:0.5];
#endif
SKSpriteNode* spritenode = [SKSpriteNode spriteNodeWithColor:color size:CGSizeMake(0, 0)];
CGFloat totalheight = 0;
CGFloat maxwidth = 0;
NSMutableArray* labels = [NSMutableArray array];
for (NSUInteger i = 0; i < strings.count; i++) {
NSString* str = [strings objectAtIndex:i];
const BOOL ISEMPTYLINE = [str isEqualToString:@""];
if (!ISEMPTYLINE) {
SKLabelNode* label = [SKLabelNode labelNodeWithFontNamed:fontname];
label.text = str;
label.fontColor = colorFont;
label.fontSize = SIZEFONT;
const CGSize SIZEOFLABEL = [label calculateAccumulatedFrame].size;
if (SIZEOFLABEL.width > maxwidth)
maxwidth = SIZEOFLABEL.width;
totalheight += SIZEOFLABEL.height;
[labels addObject:label];
}
else {
totalheight += EMPTYLINEHEIGHT;
[labels addObject:[NSNull null]];
}
if (i + 1 < strings.count)
totalheight += VERTICALMARGIN;
}
spritenode.size = CGSizeMake(maxwidth, totalheight);
//DLog(@"spritenode total size: %@", NSStringFromSize(spritenode.size));
CGFloat y = spritenode.size.height * 0.5;
const CGFloat X = 0;
for (NSUInteger i = 0; i < strings.count; i++) {
id obj = [labels objectAtIndex:i];
if ([obj isKindOfClass:SKLabelNode.class]) {
SKLabelNode* label = obj;
label.verticalAlignmentMode = SKLabelVerticalAlignmentModeTop;
label.position = ccp(X, y);
[spritenode addChild:label];
const CGSize SIZEOFLABEL = [label calculateAccumulatedFrame].size;
y -= SIZEOFLABEL.height;
}
else {
y -= EMPTYLINEHEIGHT;
}
if (i + 1 < labels.count)
y -= VERTICALMARGIN;
}
return spritenode;
}
Btw вам понадобится
static inline CGPoint ccp( CGFloat x, CGFloat y )
{
return CGPointMake(x, y);
}
Ответ 5
Итак, после небольшого исследования я узнал, что SkLabelNode не предназначен для задействования многострочных строк. Поскольку функциональность ограничена SKLabelNode, имеет смысл просто использовать UILabel, чтобы удерживать место вашего текста. Изучение того, как плавно внедрять элементы пользовательского интерфейса в набор спрайтов, облегчило жизнь. Элементы пользовательского интерфейса создаются программно и добавляются в вашу сцену с помощью
[self.view addsubview:(your UIelement)];
Итак, все, что вам нужно сделать
1.Инициализировать экземпляр UIelement в этом случае UIlabel
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
label.backgroundColor = [UIColor whiteColor];
label.textColor = [UIColor blackColor];
label.text = @"helllllllllo";
2. После того, как вы создали свой UIelement, просто добавьте его В ПРОСМОТР. Используя описанный выше метод
3. Я счел важным отметить, что элементы пользовательского интерфейса и элементы SK не взаимодействуют друг с другом, когда дело касается позиционирования. Есть несколько простых методов, таких как convertPointToView:
-(CGPoint)convertPointToView(CGPoint);
Чтобы помочь, когда дело доходит до конвертирования точек. Надеюсь, это помогло удачи!
Ответ 6
Я написал решение для Swift 3.
Демонстрационный проект Xcode доступен в проекте GitHub с открытым исходным кодом: https://github.com/benmorrow/Multilined-SKLabelNode
Вот расширение SKLabelNode
:
extension SKLabelNode {
func multilined() -> SKLabelNode {
let substrings: [String] = self.text!.components(separatedBy: "\n")
return substrings.enumerated().reduce(SKLabelNode()) {
let label = SKLabelNode(fontNamed: self.fontName)
label.text = $1.element
label.fontColor = self.fontColor
label.fontSize = self.fontSize
label.position = self.position
label.horizontalAlignmentMode = self.horizontalAlignmentMode
label.verticalAlignmentMode = self.verticalAlignmentMode
let y = CGFloat($1.offset - substrings.count / 2) * self.fontSize
label.position = CGPoint(x: 0, y: -y)
$0.addChild(label)
return $0
}
}
}
Вот как вы это используете:
let text = "hot dogs\ncold beer\nteam jerseys"
let singleLineMessage = SKLabelNode()
singleLineMessage.fontSize = min(size.width, size.height) /
CGFloat(text.components(separatedBy: "\n").count) // Fill the screen
singleLineMessage.verticalAlignmentMode = .center // Keep the origin in the center
singleLineMessage.text = text
let message = singleLineMessage.multilined()
message.position = CGPoint(x: frame.midX, y: frame.midY)
message.zPosition = 1001 // On top of all other nodes
addChild(message)
Вот как выглядит приложение:
![simulator screenshot multiline SKLabelNode]()
Ответ 7
Альтернативой является создание растровой версии текста, затем использование полученного изображения с помощью SKSpriteNode.
Это проще, чем кажется.
В примере предположим, что у нас есть строка или атрибутная строка и переменная CGSize с размером результирующей текстовой области.
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
// Assuming size is in actual pixels. Multiply size by the retina scaling
// factor if not.
CGContextRef context = CGBitmapContextCreate(NULL, (size_t)round(size.width), (size_t)round(size.height), 8, (size_t)round(size.width) * 4, rgbColorSpace, (CGBitmapInfo)kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(rgbColorSpace);
// Draw text, potentially flipping the coordinate system before
// (depending on methods you use).
// Make sure that you draw the font twice as big for retina.
// E.g. [@"My text" drawInRect:rect withAttributes:attr];
// Once we have drawn the text, simply extract the image and
// Make a texture from it.
CGImageRef image = CGBitmapContextCreateImage(context);
SKTexture *texture = [SKTexture textureWithCGImage:image];
CGImageRelease(image);
CGContextRelease(context);
// Texture created, so make a sprite node to use it.
SKSpriteNode *node = [self node];
node.texture = texture;
// Set the node size to the size in non-retina pixels, so if size was with
// scale factor already multiplied in, then we would need to divide by the scale
// factor.
node.size = size;
Ответ 8
Здесь просто для того, чтобы внести свой вклад в мое решение. Я нахожусь в том же самом желании - сделать многострочные линии SKLabelNode из длинной строки. Создание его по одному и ручное позиционирование их нецелесообразно. Поэтому я сделал более простой способ сделать многострочный SKLabelNode. Этот метод использует SKLabelNodes (и не записывает текст в изображение).
Пожалуйста, см. мое решение, если вы заинтересованы:
http://xcodenoobies.blogspot.com/2014/12/multiline-sklabelnode-hell-yes-please-xd.html
Результат:
![enter image description here]()
Ответ 9
Здесь много хороших решений, но я не видел ни одного написанного быстро, поэтому идем. эта функция будет принимать одну длинную строку и разбить ее там, где вы поместите \n символов.
func createMultiLineText(textToPrint:String, color:UIColor, fontSize:CGFloat, fontName:String, fontPosition:CGPoint, fontLineSpace:CGFloat)->SKNode{
// create node to hold the text block
var textBlock = SKNode()
//create array to hold each line
let textArr = textToPrint.componentsSeparatedByString("\n")
// loop through each line and place it in an SKNode
var lineNode: SKLabelNode
for line: String in textArr {
lineNode = SKLabelNode(fontNamed: fontName)
lineNode.text = line
lineNode.fontSize = fontSize
lineNode.fontColor = color
lineNode.fontName = fontName
lineNode.position = CGPointMake(fontPosition.x,fontPosition.y - CGFloat(textBlock.children.count ) * fontSize + fontLineSpace)
textBlock.addChild(lineNode)
}
// return the sknode with all of the text in it
return textBlock
}
Ответ 10
Начиная с iOS 11/macOS 10.13, SKLabelNode
имеет свойство numberOfLines
которое ведет себя так же, как и UILabel
. По умолчанию он установлен на 1. Если вы установите его на ноль, вы можете иметь неограниченное количество строк. Смотрите также lineBreakMode
и preferredMaxLayoutWidth
. Я подумал, что здесь стоит указать это на тот случай, если кто-нибудь зайдет на эту страницу до того, как увидит документацию Apple. Если ваша минимальная цель сборки - iOS 11/macOS 10.13, вам не нужны вспомогательные методы, опубликованные выше.
Ответ 11
Как и многие другие, я сам реализовал решение этой проблемы. Это простой подкласс SKLabelNode, который можно использовать в качестве замены обычного SKLabelNode
. Я нахожу подклассификацию наилучшего подхода для этой функциональности, поскольку я использую ее "везде" "все" время...
Все это доступно на github (для всех, кто интересуется), но основной смысл таков: он отделяет строку и создает регулярные экземпляры SKLabelNode и объявляют их как дочерние элементы node. Это делается всякий раз, когда вызывается setText:
:
- (void)setText:(NSString *)text{
self.subNodes = [self labelNodesFromText:text];
[self removeAllChildren];
for (SKLabelNode *childNode in self.subNodes) {
[self addChild:childNode];
}
_text = @""; // (synthesized in the implementation)
}
Здесь создаются подносы ярлыков:
- (NSArray *)labelNodesFromText:(NSString *)text{
NSArray *substrings = [text componentsSeparatedByString:@"\n"];
NSMutableArray *labelNodes = [[NSMutableArray alloc] initWithCapacity:[substrings count]];
NSUInteger labelNumber = 0;
for (NSString *substring in substrings) {
SKLabelNode *labelNode = [SKLabelNode labelNodeWithFontNamed:self.fontName];
labelNode.text = substring;
labelNode.fontColor = self.fontColor;
labelNode.fontSize = self.fontSize;
labelNode.horizontalAlignmentMode = self.horizontalAlignmentMode;
labelNode.verticalAlignmentMode = self.verticalAlignmentMode;
CGFloat y = self.position.y - (labelNumber * self.fontSize * kLineSpaceMultiplier); // kLineSpaceMultiplier is a float constant. 1.5 is the value I have chosen
labelNode.position = CGPointMake(self.position.x, y);
labelNumber++;
[labelNodes addObject:labelNode];
}
return [labelNodes copy];
}
Как вы могли заметить, у меня также есть свойство subNodes (array). Это пригодится в другом месте, так как полная реализация также позволяет изменять любые свойства с помощью регулярного синтаксиса SKLabelNode
. (Text, fontName, fontSize, выравнивание и т.д.)
Ответ 12
Если кому-то интересно, я создал лучший SKLabelNode
, называемый SKLabelNodePlus
, который имеет многострочную поддержку, такую как Chris Allwein, но также имеет другие функции, которые я нахожу довольно полезными.
Проверьте это на GitHub:
https://github.com/MKargin0/SKLabelNodePlus
Ответ 13
Использование https://github.com/downrightsimple/DSMultilineLabelNode и Как написать текст на изображении в Objective-C (iOS )? для справки это то, что я сделал для быстрого и грязного способа получения текстового обтекания SKNode (Xcode 7.1.1):
-(SKNode*)getWrappingTextNode:(NSString*)text maxWidth:(CGFloat)width {
UIImage *img = [self drawText:text widthDimension:width];
return [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImage:img]];
}
-(UIImage*)drawText:(NSString*)text widthDimension:(CGFloat)width {
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
paragraphStyle.alignment = NSTextAlignmentLeft; //or whatever alignment you want
UIFont *font = [UIFont fontWithName:@"Verdana" size:22]; //or whatever font you want
NSDictionary *att = @{NSFontAttributeName:font, NSParagraphStyleAttributeName: paragraphStyle};
//using 800 here but make sure this height is greater than the potential height of the text (unless you want a max-height I guess but I did not test max-height)
CGRect rect = [text boundingRectWithSize:CGSizeMake(width, 800) options:NSStringDrawingUsesLineFragmentOrigin attributes:att context:nil];
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0f);
[text drawInRect:rect withAttributes:att];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Ответ 14
Вот быстрая и простая функция, которую я написал, чтобы облегчить жизнь.
Шаг 1) Перейдите в строку, получите SKSpriteNode.
Шаг 2) Добавьте спрайт node в сцену.
/******************************************************************************/
- (SKSpriteNode*) ConvertString: (NSString*) str
WithFontSize: (NSInteger) font_size
ToParagraphWithSize: (CGSize) para_size
{
SKSpriteNode* paragraph = [[SKSpriteNode alloc] initWithColor: [SKColor clearColor]
size: para_size];
// Set the anchor point to the top left corner. This is where English
// paragraphs usually start
paragraph.anchorPoint = CGPointMake(0,1);
// Create an array to hold multilple sub strings. These sub strings will
// become multiple SKLabels that will be added to the paragraph sprite node
// created above
NSMutableArray* str_arr = [[NSMutableArray alloc] init];
// Lets separate words by a single space.
NSArray* word_arr = [str componentsSeparatedByString:@" "];
// 50% is an approximate character height to width ratio. Change this
// number to adjust the number of characters per line you would like.
// Increase it if you have a lot of capitol W's
float est_char_width = font_size * 0.50;
NSInteger num_char_per_line = para_size.width / est_char_width;
// For every word in the original string, make sure it fits on the line
// then add it to the string array.
NSString* temp_str = @"";
for (NSString* word in word_arr)
{
if ((NSInteger)word.length <= num_char_per_line - (NSInteger)temp_str.length)
{
temp_str = [NSString stringWithFormat:@"%@ %@", temp_str, word];
}
else
{
[str_arr addObject: temp_str];
temp_str = word;
}
}
[str_arr addObject: temp_str];
// For every sub string, create a label node and add it to the paragraph
for (int i = 0; i < str_arr.count; i++)
{
NSString* sub_str = [str_arr objectAtIndex: i];
SKLabelNode* label = [self CreateLabelWithText: sub_str];
label.fontSize = 14;
label.position = CGPointMake(0, -(i+1) * font_size);
[paragraph addChild: label];
}
return paragraph;
}
/******************************************************************************/
- (SKLabelNode*) CreateLabelWithText: (NSString*) str
{
enum alignment
{
CENTER,
LEFT,
RIGHT
};
SKLabelNode* label;
label = [SKLabelNode labelNodeWithFontNamed:@"ChalkboardSE-Light"];
label.name = @"label_name";
label.text = str;
label.zPosition = 1;
label.horizontalAlignmentMode = LEFT;
label.fontColor = [SKColor whiteColor];
return label;
}
Ответ 15
Я написал метод утилиты, чтобы взять строку и разделить ее на массив строк с заданной максимальной длиной. Он автоматически заканчивает каждую строку целым словом и удаляет ведущие пробелы. Надеюсь, это поможет кому-то!
- (NSArray*)linesFromString:(NSString*)string withMaxLineLength:(int)maxLineLength;
{
NSMutableArray *lines = [NSMutableArray arrayWithCapacity:1];
BOOL gotLine = NO;
BOOL doneFormat = NO;
BOOL endOfString = NO;
int innerLoops = 0;
int outerLoops = 0;
int lineIndex = 0;
int currentStringIndex = 0;
int stringLength = (int)[string length];
int rangeLength = maxLineLength;
NSString *line;
NSString *testChar;
NSString *testChar2;
while (!doneFormat) {
outerLoops++;
while (!gotLine) {
endOfString = NO;
innerLoops++;
line = [string substringWithRange:NSMakeRange(currentStringIndex, rangeLength)];
testChar = [line substringWithRange:NSMakeRange(0, 1)];
if (currentStringIndex + rangeLength > [string length] - 1) {
endOfString = YES;
} else {
testChar2 = [string substringWithRange:NSMakeRange(currentStringIndex + rangeLength, 1)];
}
//If the line starts with a space then advance 1 char and try again.
if ([testChar isEqualToString:@" "]) {
currentStringIndex++;
// If we were at the end of the string then reduce the rangeLength as well.
if (endOfString) {
rangeLength--;
}
// else, if this line ends at the end of a word (or the string) then it good. ie next char in the string is a space.
} else if ([testChar2 isEqualToString:@" "] || endOfString) {
gotLine = YES;
currentStringIndex += [line length];
// else, make the line shorter by one character and try again
} else if (rangeLength > 1){
rangeLength--;
// Otherwise the word takes up more than 1 line so use it all.
} else {
line = [string substringWithRange:NSMakeRange(currentStringIndex, maxLineLength)];
currentStringIndex += [line length];
gotLine = YES;
}
// Make sure we're not stuck in an endless loop
if (innerLoops > 1000) {
NSLog(@"Error: looped too long");
break;
}
}
// If we processed a line, and the line is not nil, add it to our array.
if (gotLine && line) {
[lines insertObject:line atIndex:lineIndex];
lineIndex++;
}
// Reset variables
rangeLength = maxLineLength;
gotLine = NO;
// If the current index is at the end of the string, then we're done.
if (currentStringIndex >= stringLength) {
doneFormat = YES;
// If we have less than a full line left, then reduce the rangeLength to avoid throwing an exception
} else if (stringLength - (currentStringIndex + rangeLength) < 0) {
rangeLength = stringLength - currentStringIndex;
}
// Make sure we're not stuck in an endless loop
if (outerLoops > 1000) {
NSLog(@"Error: Outer-looped too long");
break;
}
}
return lines;
}
И затем я просто позвоню и создаю несколько узлов ярлыков, чтобы добавить к моему слою node следующим образом. Я выравниваю свои метки линий внизу и левым краем кнопки2, поэтому все линии выравниваются по левому краю.
CGFloat fontSize = 30.0f;
int lineCount;
NSString *description = [product localizedDescription];
NSString *line;
NSArray *lines = [self linesFromString:description withMaxLineLength:43];
if (lines) {
lineCount = (int)[lines count];
for (int i = 0; i < lineCount; i++) {
line = [lines objectAtIndex:i];
// Create a new label for each line and add it to my SKSpriteNode layer
SKLabelNode *label = [SKLabelNode labelNodeWithFontNamed:@"Superclarendon-Black"];
label.text = line;
label.fontSize = fontSize;
label.scale = 1.0f;
label.name = @"lineLabel";
label.fontColor = [UIColor blackColor];
label.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft;
label.position = CGPointMake(button2.position.x - button2.size.width * 0.5f, button2.position.y - button2.size.height - i * fontSize * 1.1);
[layer addChild:label];
}
}
Ответ 16
Так что я знаю, что этот вопрос немного старше, но если что-то возвращается к нему, как у меня, теперь есть свойство preferredMaxLayoutWidth
lineBreakMode
numberOfLines
которое вы можете использовать вместе с lineBreakMode
и numberOfLines
:
Пример:
let longMessage = "Super super super super super super super super super long text"
let label = SKLabelNode(fontNamed: "Thonburi")
label.text = longMessage
label.fontSize = 24
label.fontColor = SKColor.black
// set preferredMaxLayoutWidth to the width of the SKScene
label.preferredMaxLayoutWidth = size.width
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 0
addChild(label)