目录
一、问题现象:
在用 Delphi 开发ios程序时,使用TLabel控件显示,会出现中英文无法水平对齐,英文字符会靠上大概少半行,看起来很不协调。
如下图所示:
出现这样的问题,从 ios 9 开始就一直存在。目前测试的11.3仍然存在这个问题!所以特写出此解决方案,以方便需要的朋友!
二、解决方案(一行代码解决ios对齐问题):
由于 Delphi 的版本比较多,不同的版本解决方案基本一致,但都是需要修该对应版本的FMX.FontGlyphs.iOS.pas文件。如果默认安装,一般该文件位于C:\Program Files (x86)\Embarcadero\Studio\22.0\source\fmx目录下,注意22.0表示版本,不同的版本这个数字不一样。
- 找到 FMX.FontGlyphs.iOS.pas 这个文件;
- 将该文件拷贝到你的工程文件目录下(也就是你正在开发的工程文件);
- 修改拷贝后的 FMX.FontGlyphs.iOS.pas 文件;
本文使用的是D11.3版本,修改 FMX.FontGlyphs.iOS.pas 这个文件中的这个过程 GetDefaultBaseline 。就是修改205行,将 Chars := 'a' ; 修改为 Chars := '中';
重点:
在工程文件中引用修改后的 FMX.FontGlyphs.iOS.pas 文件。
{$IFDEF IOS}
FMX.FontGlyphs.iOS, //IOS 对齐
{$ENDIF}
修改前文件:
Delphi
{*******************************************************}
{ }
{ Delphi FireMonkey Platform }
{ Copyright(c) 2012-2023 Embarcadero Technologies, Inc. }
{ All rights reserved }
{ }
{*******************************************************}
unit FMX.FontGlyphs.iOS;
interface
{$SCOPEDENUMS ON}
uses
System.Math, System.Types, System.Classes, System.SysUtils, System.UITypes, System.UIConsts, System.Generics.Collections,
System.Generics.Defaults, Macapi.ObjectiveC, Macapi.CoreFoundation, iOSapi.CocoaTypes, iOSapi.CoreGraphics,
iOSapi.Foundation, iOSapi.CoreText, iOSapi.UIKit, FMX.Types, FMX.Surfaces, FMX.FontGlyphs;
type
TIOSFontGlyphManager = class(TFontGlyphManager)
const
BoundsLimit = $FFFF;
private
FColorSpace: CGColorSpaceRef;
FFontRef: CTFontRef;
FColoredEmojiFontRef: CTFontRef;
FDefaultBaseline: Single;
FDefaultVerticalAdvance: Single;
procedure GetDefaultBaseline;
function GetFontDescriptor: CTFontDescriptorRef;
function CGColorCreate(const AColor: TAlphaColor): CGColorRef;
function CTFrameCreate(const APath: CGMutablePathRef; const ACharacter: string): CTFrameRef;
protected
procedure LoadResource; override;
procedure FreeResource; override;
function DoGetGlyph(const ACharacter: UCS4String; const Settings: TFontGlyphSettings;
const UseColorfulPalette: Boolean): TFontGlyph; override;
function DoGetBaseline: Single; override;
function IsColorfulCharacter(const ACharacter: UCS4String): Boolean; override;
public
constructor Create;
destructor Destroy; override;
end;
implementation
uses
System.Character, System.Math.Vectors, Macapi.Helpers, FMX.Graphics, FMX.Consts, FMX.Utils;
//........ 此处省略了代码
procedure TIOSFontGlyphManager.GetDefaultBaseline;
var
Chars: string;
Str: CFStringRef;
Frame: CTFrameRef;
Attr: CFMutableAttributedStringRef;
Path: CGMutablePathRef;
Bounds: CGRect;
FrameSetter: CTFramesetterRef;
// Metrics
Line: CTLineRef;
Lines: CFArrayRef;
Runs: CFArrayRef;
Run: CTRunRef;
Ascent, Descent, Leading: CGFloat;
BaseLinePos: CGPoint;
begin
Path := CGPathCreateMutable();
Bounds := CGRectMake(0, 0, BoundsLimit, BoundsLimit);
CGPathAddRect(Path, nil, Bounds);
Chars := 'a';
Str := CFStringCreateWithCharacters(kCFAllocatorDefault, PChar(Chars), 1);
Attr := CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CFAttributedStringReplaceString(Attr, CFRangeMake(0, 0), Str);
CFAttributedStringBeginEditing(Attr);
try
// Font
if FFontRef <> nil then
CFAttributedStringSetAttribute(Attr, CFRangeMake(0, 1), kCTFontAttributeName, FFontRef);
finally
CFAttributedStringEndEditing(Attr);
end;
FrameSetter := CTFramesetterCreateWithAttributedString(CFAttributedStringRef(Attr));
CFRelease(Attr);
Frame := CTFramesetterCreateFrame(FrameSetter, CFRangeMake(0, 0), Path, nil);
CFRelease(FrameSetter);
CFRelease(Str);
// Metrics
Lines := CTFrameGetLines(Frame);
Line := CTLineRef(CFArrayGetValueAtIndex(Lines, 0));
Runs := CTLineGetGlyphRuns(Line);
Run := CFArrayGetValueAtIndex(Runs, 0);
CTRunGetTypographicBounds(Run, CFRangeMake(0, 1), @Ascent, @Descent, @Leading);
CTFrameGetLineOrigins(Frame, CFRangeMake(0, 0), @BaseLinePos);
FDefaultBaseline := BoundsLimit - BaseLinePos.y;
FDefaultVerticalAdvance := FDefaultBaseline + Descent;
CFRelease(Frame);
CFRelease(Path);
end;
//........ 此处省略了代码
修改后文件:
Delphi
{*******************************************************}
{ }
{ Delphi FireMonkey Platform }
{ Copyright(c) 2012-2023 Embarcadero Technologies, Inc. }
{ All rights reserved }
{ }
{*******************************************************}
unit FMX.FontGlyphs.iOS;
interface
{$SCOPEDENUMS ON}
uses
System.Math, System.Types, System.Classes, System.SysUtils, System.UITypes, System.UIConsts, System.Generics.Collections,
System.Generics.Defaults, Macapi.ObjectiveC, Macapi.CoreFoundation, iOSapi.CocoaTypes, iOSapi.CoreGraphics,
iOSapi.Foundation, iOSapi.CoreText, iOSapi.UIKit, FMX.Types, FMX.Surfaces, FMX.FontGlyphs;
type
TIOSFontGlyphManager = class(TFontGlyphManager)
const
BoundsLimit = $FFFF;
private
FColorSpace: CGColorSpaceRef;
FFontRef: CTFontRef;
FColoredEmojiFontRef: CTFontRef;
FDefaultBaseline: Single;
FDefaultVerticalAdvance: Single;
procedure GetDefaultBaseline;
function GetFontDescriptor: CTFontDescriptorRef;
function CGColorCreate(const AColor: TAlphaColor): CGColorRef;
function CTFrameCreate(const APath: CGMutablePathRef; const ACharacter: string): CTFrameRef;
protected
procedure LoadResource; override;
procedure FreeResource; override;
function DoGetGlyph(const ACharacter: UCS4String; const Settings: TFontGlyphSettings;
const UseColorfulPalette: Boolean): TFontGlyph; override;
function DoGetBaseline: Single; override;
function IsColorfulCharacter(const ACharacter: UCS4String): Boolean; override;
public
constructor Create;
destructor Destroy; override;
end;
implementation
uses
System.Character, System.Math.Vectors, Macapi.Helpers, FMX.Graphics, FMX.Consts, FMX.Utils;
//........ 此处省略了代码
procedure TIOSFontGlyphManager.GetDefaultBaseline;
var
Chars: string;
Str: CFStringRef;
Frame: CTFrameRef;
Attr: CFMutableAttributedStringRef;
Path: CGMutablePathRef;
Bounds: CGRect;
FrameSetter: CTFramesetterRef;
// Metrics
Line: CTLineRef;
Lines: CFArrayRef;
Runs: CFArrayRef;
Run: CTRunRef;
Ascent, Descent, Leading: CGFloat;
BaseLinePos: CGPoint;
begin
Path := CGPathCreateMutable();
Bounds := CGRectMake(0, 0, BoundsLimit, BoundsLimit);
CGPathAddRect(Path, nil, Bounds);
Chars := '中';
//Chars := 'a';
Str := CFStringCreateWithCharacters(kCFAllocatorDefault, PChar(Chars), 1);
Attr := CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CFAttributedStringReplaceString(Attr, CFRangeMake(0, 0), Str);
CFAttributedStringBeginEditing(Attr);
try
// Font
if FFontRef <> nil then
CFAttributedStringSetAttribute(Attr, CFRangeMake(0, 1), kCTFontAttributeName, FFontRef);
finally
CFAttributedStringEndEditing(Attr);
end;
FrameSetter := CTFramesetterCreateWithAttributedString(CFAttributedStringRef(Attr));
CFRelease(Attr);
Frame := CTFramesetterCreateFrame(FrameSetter, CFRangeMake(0, 0), Path, nil);
CFRelease(FrameSetter);
CFRelease(Str);
// Metrics
Lines := CTFrameGetLines(Frame);
Line := CTLineRef(CFArrayGetValueAtIndex(Lines, 0));
Runs := CTLineGetGlyphRuns(Line);
Run := CFArrayGetValueAtIndex(Runs, 0);
CTRunGetTypographicBounds(Run, CFRangeMake(0, 1), @Ascent, @Descent, @Leading);
CTFrameGetLineOrigins(Frame, CFRangeMake(0, 0), @BaseLinePos);
FDefaultBaseline := BoundsLimit - BaseLinePos.y;
FDefaultVerticalAdvance := FDefaultBaseline + Descent;
CFRelease(Frame);
CFRelease(Path);
end;
//........ 此处省略了代码
三、解决后效果:
已经全部水平对齐,完美解决!
四、后记:
记得当时在使用D10.1 berlin的时候,就存在这个问题。中间一直在没有用Delphi开发过ios程序,当时以为高版本的Delphi 可能会解决这个问题,没想到D11.3仍然存在这个问题,不知道是否是在什么地方配置下就可以解决(如果确实有知道的请留言告知)。幸好当时解决有记录,今天遇到问题还可以继续使用。这要感谢妈妈教我的:闲时收拾,忙时用!