5

如果文本中有视频,我将照片放在 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()

}

} --- --- ---

4

0 回答 0