Что вы хотите сделать, в основном, превратить NSAttributedString в NSMutableAttributedString.
let attributedT = // ... attributed string
let mutableT = NSMutableAttributedString(attributedString:attributedT)
Теперь вы можете вызвать addAttributes
, чтобы применить атрибуты, такие как другой шрифт, в любом желаемом диапазоне, например, все это.
Однако, к сожалению, шрифт без символической черты, такой как курсив, представляет собой другой шрифт из шрифта с этим символическим признаком. Поэтому вам понадобится утилита, которая копирует существующие символические черты из шрифта и применяет их к другому шрифту:
func applyTraitsFromFont(_ f1: UIFont, to f2: UIFont) -> UIFont? {
let t = f1.fontDescriptor.symbolicTraits
if let fd = f2.fontDescriptor.withSymbolicTraits(t) {
return UIFont.init(descriptor: fd, size: 0)
}
return nil
}
Хорошо, поэтому, вооружившись этой утилитой, давайте попробуем. Я начну с простого HTML-кода и преобразую его в атрибутную строку, как вы это делаете:
let html = "Hello world, hello
"
let data = html.data(using: .utf8)!
let att = try! NSAttributedString.init(
data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil)
let matt = NSMutableAttributedString(attributedString:att)
Как вы можете видеть, я перешел в NSMutableAttributedString, как я вам посоветовал. Теперь я буду циклически выполнять стили в терминах шрифта, изменяясь на другой шрифт, используя мою утилиту для применения существующих признаков:
matt.enumerateAttribute(
NSFontAttributeName,
in:NSMakeRange(0,matt.length),
options:.longestEffectiveRangeNotRequired) { value, range, stop in
let f1 = value as! UIFont
let f2 = UIFont(name:"Georgia", size:20)!
if let f3 = applyTraitsFromFont(f1, to:f2) {
matt.addAttribute(
NSFontAttributeName, value:f3, range:range)
}
}
Вот результат:
Очевидно, вы могли бы настроить эту процедуру еще более сложной, в зависимости от ваших потребностей в дизайне.
Я сделал это для небольшого проекта некоторое время назад, и вот упрощенное решение, которое я нашел.
Создайте новый элемент управления путем подкласса RichTextBox:
public class SynchronizedScrollRichTextBox : System.Windows.Forms.RichTextBox
{
public event vScrollEventHandler vScroll;
public delegate void vScrollEventHandler(System.Windows.Forms.Message message);
public const int WM_VSCROLL = 0x115;
protected override void WndProc(ref System.Windows.Forms.Message msg) {
if (msg.Msg == WM_VSCROLL) {
if (vScroll != null) {
vScroll(msg);
}
}
base.WndProc(ref msg);
}
public void PubWndProc(ref System.Windows.Forms.Message msg) {
base.WndProc(ref msg);
}
}
Добавьте новый элемент управления в свою форму и для каждого элемента управления явным образом уведомите другие экземпляры элемента управления о том, что его позиция vScroll изменилась , Что-то вроде этого:
private void scrollSyncTxtBox1_vScroll(Message msg) {
msg.HWnd = scrollSyncTxtBox2.Handle;
scrollSyncTxtBox2.PubWndProc(ref msg);
}
Я думаю, что этот код имеет проблемы, если все «связанные» элементы управления не имеют одинакового количества отображаемых строк.
Вариант подкласса Jay можно найти в сообщении Джозефа Кингри здесь: Синхронизация многострочных позиций текстового поля в C # . Подход Джозефа также подклассы, но не требует обработчика события _VScroll. Я использовал этот подход, чтобы выполнить 3-стороннее связывание между 3-мя блоками и добавленным WM_HSCROLL.
Спасибо Джей за ваш ответ; после еще нескольких поисков я нашел метод, описанный здесь здесь .
Сначала объявим следующие перечисления:
public enum ScrollBarType : uint {
SbHorz = 0,
SbVert = 1,
SbCtl = 2,
SbBoth = 3
}
public enum Message : uint {
WM_VSCROLL = 0x0115
}
public enum ScrollBarCommands : uint {
SB_THUMBPOSITION = 4
}
Затем добавьте внешние ссылки на GetScrollPos
и SendMessage
.
[DllImport( "User32.dll" )]
public extern static int GetScrollPos( IntPtr hWnd, int nBar );
[DllImport( "User32.dll" )]
public extern static int SendMessage( IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam );
Наконец, добавьте обработчик событий для события VScroll
соответствующего RichTextBox
:
private void myRichTextBox1_VScroll( object sender, EventArgs e )
{
int nPos = GetScrollPos( richTextBox1.Handle, (int)ScrollBarType.SbVert );
nPos <<= 16;
uint wParam = (uint)ScrollBarCommands.SB_THUMBPOSITION | (uint)nPos;
SendMessage( richTextBox2.Handle, (int)Message.WM_VSCROLL, new IntPtr( wParam ), new IntPtr( 0 ) );
}
В этом случае положение вертикальной прокрутки richTextBox2
будет синхронизировано с richTextBox1
.
[Visual Studio C # 2010 Express, v10.0.30319 на Windows 7 64-битной установке]
Я использовал решение Donut выше, но обнаружил проблему при прокрутке до конца RichTextBoxes, которые содержат много линий.
Если результат GetScrollPos()
равен >0x7FFF
, тогда, когда nPos
сдвинут, верхний бит установлен. При создании IntPtr
с результирующей переменной wParam
произойдет сбой OverflowException
. Вы можете легко проверить это следующим образом (вторая строка не сработает):
IntPtr ip = new IntPtr(0x7FFF0000);
IntPtr ip2 = new IntPtr(0x80000000);
Версия SendMessage()
, которая использует UIntPtr
, окажется решением, но я не мог получить что работать. Итак, я использую следующее:
[DllImport("User32.dll")]
public extern static int SendMessage(IntPtr hWnd, uint msg, UInt32 wParam, UInt32 lParam);
Это должно быть хорошо до 0xffff
, но после этого произойдет сбой. Я еще не испытал результат >0xffff
из GetScrollPos()
и предположил, что User32.dll вряд ли будет иметь 64-битную версию SendCommand()
, но любые решения этой проблемы будут весьма полезны.
page down
илиpage up
- прокруткой, нажимая клавиши со стрелками – twnaing 30 May 2011 в 04:54