Thursday, 8 August 2019

textView with exclusionPaths and video

I put photos inside a textView if there is video in the text, then I replace the video's photo with the player, the problem arose with the player, I can't understand why the player position is not correct, I noticed replaceCharacters does not work correctly, copy? .replaceCharacters (in: videoRange [i - wrong], with: "this is correct position") replaces only the first photo and copy? .replaceCharacters (in: videoRange [i - wrong], with: "+") all but with the wrong position, example how replaceCharacters work is below

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 {

                // get CGRect of player
                var rect = self.boundingRect(forCharacterRange: videoRange[i - wrong])!
                print(rect, "rect")
                if isVideo[i] {

                    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)
    }

}

--- --- ---



from textView with exclusionPaths and video

No comments:

Post a Comment