如果文本中有视频,我将照片放在 textView 中,然后我用播放器替换视频的照片,播放器出现问题,我不明白为什么播放器位置不正确,我注意到replaceCharacters
无法正常工作,
copy? .replaceCharacters (in: videoRange [i - wrong], with: "this is correct position")
仅替换第一张照片,copy? .replaceCharacters (in: videoRange [i - wrong], with: "+")
但位置错误,示例replaceCharacters
如下
HTMLString 是
<p>
Лева Би-2 – настоящее имя Игорь (Егор) Михайлович Бортник.
</p>
<p>
<iframe> <a href='https://www.youtube.com/watch?v=nXqyXRgkI_0'><img src="https://img.youtube.com/vi/nXqyXRgkI_0/0.jpg" alt="" width="600" /></a></iframe>
</p>
<p>
Детство музыканта прошло в Африке. Отец Егора преподавал радиофизику в университете Конго. Местные ребята прозвали мальчика Львом за то, что он носил львиный клык не снимая. Позже и сам Егор стал себя так называть.
</p>
<p>
Игорем Лева стал случайно: в Израиле чиновники не смогли верно написать данное при рождении имя Егор и Бортник получил новый документ, где было записано имя Игорь.
</p>
<p>
В 1985-ом году Лева знакомится с Александром Уманом – в дальнейшем именуемым Шурой Би-2, и они создают коллектив «Братья по оружию», который позже сменит название на «Берег Истины», а затем на «БИ-2».
</p>
<p>
<img width="800" src="https://cyprusbutterfly.com.cy/assets/cache_image/images/news/2018-06-19_09-45_kartinkijane.ru-65955_800x400_c8e.jpg" height="400" align="middle"><br>
</p>
<p>
Шесть лет Лева прожил в Израиле, где работал строителем и каменотесом, занимался компьютерным дизайном и даже служил в армии. Потом музыкант перебрался в Австралию, где к тому времени уже обосновался Шура. Днем Лева работал начальником строительной бригады, а по вечерам вместе с Шурой давал концерты.
</p>
<p>
О возвращении на родину ребята не думали. Пока в 1998 году их российские друзья не отдали пару композиций сте с Шурой давал концерты.
</p>
<p>
О возвращении на родину ребята не думали. Пока в 1998 году их российские друзья не отдали пару композиций \302«БИ-2» на «Наше радио». Песня «Варвара» «взорвала» эфир, а группа мгновенно стала популярна в России.
</p>
<p>
<iframe> <a href='https://www.youtube.com/watch?v=mPK4EXCto-I'><img src="https://img.youtube.com/vi/mPK4EXCto-I/0.jpg" alt="" width="600" /></a>
</iframe>
</p>
<p>
Настоящий успех пришел после выхода в свет фильма «Брат-2». Картина стала блокбастером, а «БИ-2» – признанными звездами.
</p>
<p>
<iframe> <a href='https://www.youtube.com/watch?v=Me4lyuH0U-A'><img src="https://img.youtube.com/vi/Me4lyuH0U-A/0.jpg" alt="" width="600" /></a>
</iframe>
</p>
<p>
У Левы есть сын от первого брака – Федор, и двое сыновей от второй жены. Мальчикам дали еврейские имена – Авив, что в переводе с иврита означаеЛевы есть сын от первого брака – Федор, и двое сыновей от второй жены. Мальчикам дали еврейские имена – Авив, что в переводе с иврита означае\321т «Весна», и Давид. Свадьбу Лева сыграл в один день с Шурой.
</p>
<p>
Музыкант отлично владеет ивритом. Собирает модели автомобилей. Коллекция артиста насчитывает уже более восьмисот экземпляров.
</p>
<br>
extension UITextView {
// conver range to CGRect
func boundingRect(forCharacterRange range: NSRange) -> CGRect? {
guard let attributedText = attributedText else { return nil }
let textStorage = NSTextStorage(attributedString: attributedText)
let layoutManager = NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
let textContainer = NSTextContainer(size: intrinsicContentSize)
textContainer.lineFragmentPadding = 0
//layoutManager.hyphenationFactor = 1.0
layoutManager.addTextContainer(textContainer)
var glyphRange = NSRange()
layoutManager.characterRange(forGlyphRange: range, actualGlyphRange: &glyphRange)
var rect = layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
rect.size.height = self.frame.width / 1.7
rect.size.width = self.frame.width
rect.origin.x = 0
return rect
}
// add image and video in text
func convertToInlineImageFormat(htmlString:String){
let text = videoInText(htmlString: htmlString)
let videoId = text.1
let HTMLString = text.0
guard let data = HTMLString.data(using: String.Encoding.unicode, allowLossyConversion: true) else { return }
let content = try! NSMutableAttributedString(
data: data,
options: [ .documentType: NSAttributedString.DocumentType.html],
documentAttributes: nil)
var videoRange = [NSRange]()
var height = [CGFloat]()
var isVideo = [Bool]()
let fontDesc = UIFont(name:"roboto-light", size: 19)
content.addAttribute(NSAttributedString.Key.font, value: fontDesc!, range: NSRange(location: 0, length: content.length))
content.enumerateAttribute(NSAttributedString.Key.attachment, in: NSRange(location: 0, length: content.length), options: [], using: {(value,range,stop) -> Void in
if (value is NSTextAttachment) {
let attachment: NSTextAttachment? = (value as? NSTextAttachment)
let fileWrapper = attachment?.fileWrapper
DispatchQueue.main.async {
let width = self.frame.size.width
if fileWrapper?.preferredFilename == "0.jpg" {
isVideo.append(true)
videoRange.append(range)
height.append(self.frame.width / 1.7)
attachment?.bounds.size = CGSize(width: width, height: self.frame.width / 1.7)
} else {
isVideo.append(false)
height.append(0)
attachment?.bounds.origin = CGPoint(x: 0, y: 20)
attachment?.bounds.size = CGSize(width: width, height: CGFloat(width/(attachment?.bounds.width ?? width) * (attachment?.bounds.height ?? width)))
}
}
}
})
DispatchQueue.main.async {
self.textContainer.lineFragmentPadding = 0 // отступы внутри textView
self.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.attributedText = content
var wrong = 0
for i in 0 ..< height.count {
if isVideo[i] {
// get CGRect of player
var rect = self.boundingRect(forCharacterRange: videoRange[i - wrong])!
print(rect, "rect")
for i in 0 ..< i {
rect.origin.y += height[i]
}
//place for player
let imgRect : UIBezierPath = UIBezierPath(rect:rect)
if self.textContainer.exclusionPaths == [] {
self.textContainer.exclusionPaths = [imgRect]
} else {
self.textContainer.exclusionPaths.append(imgRect)
}
//add player
YouTubeManager.shared.addVideo(textView: self, id: videoId[i - wrong], rect: rect)
// replace image of video
let copy = self.attributedText.mutableCopy() as? NSMutableAttributedString
copy?.replaceCharacters(in: videoRange[i - wrong], with: "this is correct position")
self.attributedText = copy
} else {
wrong += 1
}
}
}
}
//remove iframe and return video`s id
func videoInText(htmlString:String)-> (String, [String]){
func setText(text: String) -> (String, [String]) {
return formatString(text: text);
}
//main function that adds the youtube frame
func formatString(text: String) -> (String, [String]) {
let iframe_texts = matches(for: ".*iframe.*", in: text);
var new_text = text;
var video_id = [String]()
if iframe_texts.count > 0 {
for iframe_text in iframe_texts {
let iframe_id = matches(for: "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)", in: iframe_text);
if iframe_id.count > 0 { //just in case there is another type of iframe
new_text = new_text.replacingOccurrences(of: iframe_text, with:"<a href='https://www.youtube.com/watch?v=\(iframe_id[0])'><img src=\"https://img.youtube.com/vi/" + iframe_id[0] + "/0.jpg\" alt=\"\" width=\"600\" /></a>");
video_id.append(iframe_id[0])
}
}
} else {
// print("there is no iframe in this text");
}
return (new_text, video_id)
}
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex, options: .caseInsensitive)
let nsString = text as NSString
let results = regex.matches(in: text, range: NSRange(location: 0, length: nsString.length))
return results.map { nsString.substring(with: $0.range)}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
return setText(text: htmlString)
}
}
YouTubeManager 在 textView 中添加和删除播放器。 github 链接 https://github.com/mukeshydv/YoutubePlayerView
class YouTubeManager {
static var shared = YouTubeManager()
private var id = [UITextView:[YoutubePlayerView]]()
open func addVideo(textView:UITextView, id:String, rect:CGRect){
let spacerView : YoutubePlayerView = YoutubePlayerView.init(frame: rect)
spacerView.loadWithVideoId(id)
textView.addSubview(spacerView)
if self.id[textView] != nil {
self.id[textView]?.append(spacerView)
} else {
self.id[textView] = [spacerView]
}
}
open func removeVideo(textView:UITextView) {
textView.textContainer.exclusionPaths = []
textView.attributedText = nil
guard let id = self.id[textView] else { return }
for i in id {
let result = i.getWebView()
result.stopLoading()
i.removeFromSuperview()
}
self.id[textView]?.removeAll()
}