NSTextAttachment에 추가된 이미지 색상을 변경할 때 매혹

UILAbel에 이미지를 표시하고 색상을 변경하는 기사는 별의 수를 나타냅니다.
대체로 아래의 느낌으로 실현할 수 있다.
*UIIMage.withRenderingMode(.alwaysTemplate) 생성
* NSTextAttachment 이미지로 설정
* NSMutableAttributedString에 NSAttributedString 적용
*NSMutableAttributedString에서 addAttributedString(u,value:range:) NSAttributedString.Key.foregroundColor를 포인트로 UIColor 설정
이런
        let image = UIImage(named: "foo")!
        let attributedString = NSMutableAttributedString(string:"")
        let templateImage = image.withRenderingMode(.alwaysTemplate)

        let textAttachment = NSTextAttachment()
        textAttachment.image = templateImage
        textAttachment.bounds = CGRect(x: 0, y: 0, width: 20, height: 20)

        let attrString = NSAttributedString(attachment: textAttachment)
        attributedString.append(attrString)

        attributedString.addAttribute(
            NSAttributedString.Key.foregroundColor,
            value: UIColor.green,
            range: NSRange(location: 0, length: attributedString.length))

        label.attributedText = attributedString
하지만 나는 몇 번이나 썼어야 할 이 코드에 두 시간 정도 빠져들었다.
  • Xcode10.1
  • Swift4.2
  • 문제


    설정된 UIColor는 전혀 반영되지 않았습니다.

    원인


    NSMutableAttributedString의 시작이 문자열인 경우에는 문제가 없습니다.
    그 결과 NSTextAttachment에서 시작된 상황에서addAttachment를 해도 UIColor는 이미지에 적용되지 않습니다.
    NSMutableAttributedString 컨텐츠
    "😀text
    "text😀"그러면문제없어요.

    문제가 있다



    문제없다



    대책


    NSMutableAttributedString의 시작 부분에 문자열(공간)을 추가하여 회피합니다.
            let image = UIImage(named: "foo")!
            let attributedString = NSMutableAttributedString(string:"")  // <- ここに入れてもOK
            let templateImage = image.withRenderingMode(.alwaysTemplate)
    
            let textAttachment = NSTextAttachment()
            textAttachment.image = templateImage
            textAttachment.bounds = CGRect(x: 0, y: 0, width: 20, height: 20)
    
            let attrString = NSAttributedString(attachment: textAttachment)
            attributedString.append(attrString)
    
            attributedString.insert(NSAttributedString(string: " "), at: 0)  // <- これ
    
            attributedString.addAttribute(
                NSAttributedString.Key.foregroundColor,
                value: UIColor.green,
                range: NSRange(location: 0, length: attributedString.length))
    
            label.attributedText = attributedString
    
    이렇게 하면 한 가지 일이 해결될 줄 알았는데 함정이 있을 줄은 생각지도 못했다.
    처음에 문자열을 넣어서 시작을 뒤로 하는 문자열은 이미지만 있는 경우보다 베이스라인이 조금 높습니다.
    밀집된 레이아웃의 응용 프로그램이라면 위쪽이 끊어지고 아래에 이상한 공간이 생길 수 있다
    대체적으로 코드상 매우 메스껍다.

    첫 번째는 그림입니다.



    문자열로 시작


    문제는 회피되었지만, 앞에는 공간 (문자열) 과baseline이 미묘하게 위에 있습니다.

    대책


    베이스라인에 맞추려고 노력해도 맨 앞에 공간이 있을 수는 없다.
    또한 AttributedString의 처리에 의존하여 레이아웃을 하는 것도 앞으로 고통스러운 시선을 보게 될 것이다.
    촌스럽지만 그림 자체를 색깔로 덮어쓰는 방법이다.
    class HogeView {
        func fuga() {
            let image = UIImage(named: "foo")!
            let attributedString = NSMutableAttributedString(string:"")
            let templateImage = image.withRenderingMode(.alwaysTemplate)
    
            let textAttachment = NSTextAttachment()
            textAttachment.image = templateImage.overray(with: .red)
            textAttachment.bounds = CGRect(x: 0, y: 0, width: 20, height: 20)
    
            let attrString = NSAttributedString(attachment: textAttachment)
            attributedString.append(attrString)
    
            label.attributedText = attributedString
        }
    }
    
    extension UIImage {
         func overray(with fillColor: UIColor) -> UIImage {
    
              // using scale correctly preserves retina images
             UIGraphicsBeginImageContextWithOptions(size, false, scale)
             let context: CGContext! = UIGraphicsGetCurrentContext()
             assert(context != nil)
    
              // correctly rotate image
             context.translateBy(x: 0, y: size.height)
             context.scaleBy(x: 1.0, y: -1.0)
    
              let rect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
    
              // draw tint color
             context.setBlendMode(.normal)
             fillColor.setFill()
             context.fill(rect)
    
              // mask by alpha values of original image
             context.setBlendMode(.destinationIn)
             context.draw(self.cgImage!, in: rect)
    
              let image = UIGraphicsGetImageFromCurrentImageContext()
             UIGraphicsEndImageContext()
             return image!
         }
     }
    

    결과



    감상


    글의 첫머리에 아이콘 같은 인상을 주는 경우가 많은 것 같아요.

    좋은 웹페이지 즐겨찾기