How to check two NSAttributedString.key of a NSAttributed string for equality? - swift4.2

I am implementing a set game where my cards are buttons which consist of NSAttributedString as attributed titles. To check whether three selected cards make a set or not I wanted to compare the attributes on each attributed string (which are attributed titles on the buttons) since each attribute represent a property. This can't be done as I am getting a compilation error while comparing them.
let at: [NSAttributedString.Key : Any] = [
.strokeColor : UIColor.blue,
.foregroundColor : UIColor.white,
.strokeWidth : -7.0,
.font : UIFont.systemFont(ofSize: 35)
]
let s = NSAttributedString(string: "●", attributes: at)
let attribute: [NSAttributedString.Key : Any] = [
.strokeColor : UIColor.blue,
.foregroundColor : UIColor.white,
.strokeWidth : -7.0,
.font : UIFont.systemFont(ofSize: 35)
]
let a = NSAttributedString(string: "▲", attributes: attribute)
if(s.attributes(at: 0, effectiveRange: nil) & (a.attributes(at: 0, effectiveRange: nil)) // ## Compilation Error: Any doesn't conform to Eqautable Protocol ##

You can use NSAttributedString instance func isEqual(to other: NSAttributedString) -> Bool method for these purpose.

Related

call child data from firebase

Good evening,
I am trying to call data to post onto a card very similar to tinder. When I run my code, everything works and I see the print statement in the console. However the card view shows a default image with default text.
I was wondering if anyone has encountered this issue and could possibly help explain what I am doing wrong.
fileprivate func fetchUsersFromDatabase() {
Database.database().reference().child("JobPost").observeSingleEvent(of: .value, with: {(Snapshot) in
if let eachDict = Snapshot.value as? NSDictionary{
for each in eachDict{
//I think the issue is caused by the let post = poster
let post = Poster(dictionary: Snapshot.value as! [String : Any])
self.cardViewModels.append(post.toCardViewModel())
print(each.value )
}
}
self.setupDummyCards()
}, withCancel: {(Err) in
})
}
// the struct is in an extension file.
struct Poster: ProducesCardViewModel{
var jobName : String?
var price: Int?
var postName: String?
var ImageUrl1: String?
var uid: String?
init(dictionary: [String: Any]) {
self.price = dictionary["cost"] as? Int
self.jobName = dictionary["category"] as? String
self.postName = dictionary["description"] as? String ?? ""
self.ImageUrl1 = dictionary["JobImageUrl"] as? String ?? ""
self.uid = dictionary["fromId"] as? String ?? ""
}
func toCardViewModel() -> CardViewModel {
let attributedText = NSMutableAttributedString(string: jobName ?? "", attributes: [.font: UIFont.systemFont(ofSize: 32, weight: .heavy)])
let priceString = price != nil ? "\(price!)" : "N\\A"
attributedText.append(NSAttributedString(string: " \(priceString)", attributes: [.font: UIFont.systemFont(ofSize: 24, weight: .regular)]))
let jobString = jobName != nil ? jobName! : "Not available"
attributedText.append(NSAttributedString(string: "\n\(jobString)", attributes: [.font: UIFont.systemFont(ofSize: 20, weight: .regular)]))
return CardViewModel(imageNames: [ImageUrl1 ?? "" ], attributedString: attributedText, textAlignment: .left)
}
}
Example
// toCardViewModel
import UIKit
protocol ProducesCardViewModel {
func toCardViewModel() -> CardViewModel
}
class CardViewModel {
let JobimageName: [String]
let attributedString: NSAttributedString
let textAlignment: NSTextAlignment
init(imageNames: [String], attributedString: NSAttributedString, textAlignment: NSTextAlignment) {
self.JobimageName = imageNames
self.attributedString = attributedString
self.textAlignment = textAlignment
}
fileprivate var imageIndex = 0 {
didSet {
let imageName = JobimageName[imageIndex]
let image = UIImage(named: imageName)
imageIndexObserver?(imageIndex, image)
}
}
var imageIndexObserver: ((Int, UIImage?) -> ())?
func advanceToNextPhoto() {
imageIndex = min(imageIndex + 1, JobimageName.count - 1)
}
func goToPreviousPhoto() {
imageIndex = max(0, imageIndex - 1)
}
}
Thank you in advance!
// P.S I previously posted this question without lack of sufficient detail. I decided to just re post it with the quality material. I really appreciate your time.
I have figured out an answer and it was quite obvious to me. I noticed that
print(each.value )
would print the value, so I just substituted
let post = Poster(dictionary: Snapshot.value as! [String : Any])
to
let post = Poster(dictionary: each.value as! [String : Any])
and everything started to work just fine!

'advanced(by:)' is unavailable convert from swift 3 to swift 4

internal func rangeFromNSRange(_ nsRange: NSRange) -> Range<String.Index>? {
let from16 = utf16.startIndex.advanced(by: nsRange.location)
let to16 = from16.advanced(by: nsRange.length) //advanced(by:) is unavailable
if let from = String.Index(from16, within: self),
let to = String.Index(to16, within: self) {
return from ..< to
}
return nil
}
I have this file in swift 3 and I'm trying to convert it to swift 4 but I get this error and also this error
public func height(_ width: CGFloat, font: UIFont, lineBreakMode: NSLineBreakMode?) -> CGFloat {
var attrib: [String: AnyObject] = [NSAttributedStringKey.font.rawValue: font]
if lineBreakMode != nil {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = lineBreakMode!
attrib.updateValue(paragraphStyle, forKey: NSAttributedStringKey.paragraphStyle.rawValue)
}
let size = CGSize(width: width, height: CGFloat(Double.greatestFiniteMagnitude))
return ceil((self as NSString).boundingRect(with: size, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes:attrib, context: nil).height)
}
// Cannot convert value of type '[String : AnyObject]' to expected argument type '[NSAttributedStringKey : Any]?'
In Swift 4 there is an API to make Range<String.Index> from NSRange and vice versa. The String extension can be reduced to
internal func range(from nsRange: NSRange) -> Range<String.Index>? {
return Range(nsRange, in: self)
}
In Swift 4 the type of string attributes has been changed from String to NSAttributedStringKey. For example NSForegroundColorAttributeName is replaced with NSAttributedStringKey.foregroundColor. You need to change the declaration of attrib to
var attrib: [NSAttributedStringKey: Any] = [.font: font]
and to change the line to add an attribute accordingly.

How do you get text length in Swift 4?

I am not sure how to update this code:
func textWidth(text: String, font: UIFont?) -> CGFloat
{
let attributes = font?.fontDescriptor.fontAttributes
return text.size(withAttributes: attributes).width
}
Swift 4 complain:
Cannot convert value of type '[UIFontDescriptor.AttributeName : Any]?' to expected argument type '[NSAttributedStringKey : Any]?'
I don't know why they broke this, but it does not get automatically fixed.
(SWIFT 4 Updated)
func textWidth(text: String, font: UIFont?) -> CGFloat {
let attributes = font != nil ? [NSFontAttributeName: font] : [:]
return text.size(withAttributes: attributes).width
}
Hope it helps.
I guess, you want to write something like this:
(Swift 4)
func textWidth(text: String, font: UIFont?) -> CGFloat {
let attributes = font != nil ? [NSAttributedStringKey.font: font!] : [:]
return text.size(withAttributes: attributes).width
}
(Swift 3)
func textWidth(text: String, font: UIFont?) -> CGFloat {
let attributes = font != nil ? [NSFontAttributeName: font!] : [:]
return text.size(attributes: attributes).width
}
fontAttributes is not a valid input for size(withAttributes:) (size(attributes:) in Swift 3) even if the type matches.
An alternative way to do this in Swift 4, avoiding the handling of the optional, is like this:
extension UIFont {
func textWidth(s: String) -> CGFloat
{
return s.size(withAttributes: [NSAttributedStringKey.font: self]).width
}
}

Cannot convert value of type 'UInt32!' to expected argument type 'UIFontDescriptorSymbolicTraits'

I´m still trying to convert TextKit from Objective-C to Swift. It drives me (and you ?) crazy :-(
After fixing some problems, now i´m having trouble with the bold (and italic) font:
func addOrRemoveFontTraitWithName(traitName: String, andValue traitValue: UInt32, andRange selectedRange: NSRange) {
let currentAttributesDict : NSDictionary! = self.textView.textStorage.attributesAtIndex(selectedRange.location, effectiveRange: nil)
let currentFont : UIFont = currentAttributesDict .objectForKey(NSFontAttributeName) as! UIFont
...
var existingTraitsWithNewTrait : UInt32! = nil
var changedFontDescriptor : UIFontDescriptor! = nil
if fontNameAttribute.rangeOfString(traitName).location == NSNotFound {
existingTraitsWithNewTrait = fontDescriptor.symbolicTraits.rawValue | traitValue
changedFontDescriptor = fontDescriptor.fontDescriptorWithSymbolicTraits(UIFontDescriptorSymbolicTraits.TraitBold)
} else {
existingTraitsWithNewTrait = fontDescriptor.symbolicTraits.rawValue & ~traitValue
changedFontDescriptor =
fontDescriptor.fontDescriptorWithSymbolicTraits(existingTraitsWithNewTrait) // !!!!
}
In the last line i get the error
Cannot convert value of type 'UInt32!' to expected argument type 'UIFontDescriptorSymbolicTraits'
Must i convert UInt32 to UIFontDescriptorSymbolicTraits? The other way is to use .rawValue. But i dont know, how to do this. :-(
Any help is welcome!
Try UIFontDescriptorSymbolicTraits(rawValue: existingTraitsWithNewTrait) in place of existingTraitsWithNewTrait in the error line.

set and create paragraph style in swift

I am trying to port an objective C method to draw text in a PDF context to Swift.
I could convert most of the code, but the following lines are giving me a problem.
Some help in the conversion would be welcome.
Here the Objective C code:
-(void)drawText:(NSString *)textToDraw context:(CGContextRef)myPDFContext textcolor:(NSColor *)textcolor textfont:(NSFont*)textfont textbox:(CGRect)boxsize pagesize:(CGSize)pageSize {
// .........
// create paragraph style and assign text alignment to it
CTTextAlignment alignment = kCTJustifiedTextAlignment;
CTParagraphStyleSetting _settings[] = { {kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment} };
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(_settings, sizeof(_settings) / sizeof(_settings[0]));
// set paragraph style attribute
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTParagraphStyleAttributeName, paragraphStyle);
// .........
}
// The following lines are my try in Swift, but this gives errors:
func DrawText(textToDraw:String, myPDFContext:CGContextRef, textcolor:NSColor, textfont:NSFont, boxsize:CGRect, pagesize:CGSize) {
var alignment = CTTextAlignment.TextAlignmentLeft
let alignmentSetting = CTParagraphStyleSetting(spec: CTParagraphStyleSpecifier.Alignment, valueSize: sizeof(alignment), value: &alignment)
let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, 1)
//.......
If this is solved I could post the complete method in Swift.
I think this does it:
var alignment = CTTextAlignment.TextAlignmentLeft
let alignmentSetting = [CTParagraphStyleSetting(spec: .Alignment, valueSize: UInt(sizeofValue(alignment)), value: &alignment)]
let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, 1)
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTParagraphStyleAttributeName, paragraphStyle)
I made the following modifications:
I changed sizeof(alignment) to UInt(sizeofValue(alignment)) because sizeof in Swift only takes a type (such as sizeof(Int)). The UInt() is a constructor that turns the Int returned by sizeofValue into the UInt needed by this call.
I make alignmentSetting into an array so that it could be passed to an UnsafePointer of that type. This also better matches the original version.
I changed CTParagraphStyleSpecifier.Alignment to just .Alignment since the first part isn't needed since spec is of type CTParagraphStyleSpecifier.
The hardcoded 1 is OK in this case because you are passing just one value, but the more general solution would be to do:
let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, UInt(alignmentSetting.count))
Here the full working method based on the corrections and help.
func drawText(textToDraw:String, myPDFContext:CGContextRef, textcolor:NSColor, textfont:NSFont, boxsize:CGRect, pagesize:CGSize) {
let _font = NSFont(name:textfont.fontName, size:textfont.pointSize )
if (_font == nil) {
println("Font [\(textfont)] does not exist")
return
}
let myBoxWidth = boxsize.size.width
let myBoxHeight = boxsize.size.height
let myBoxxpos = boxsize.origin.x
let myBoxypos = boxsize.origin.y
let frameRect = CGRectMake(myBoxxpos, myBoxypos,myBoxWidth,myBoxHeight);
let framePath = CGPathCreateMutable()
CGPathAddRect(framePath, nil, frameRect);
// create attributed string
let attrStr = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CFAttributedStringReplaceString (attrStr, CFRangeMake(0, 0), textToDraw);
// create font
let font = CTFontCreateWithName(textfont.fontName, textfont.pointSize, nil);
var alignment = CTTextAlignment.TextAlignmentLeft
let alignmentSetting = [CTParagraphStyleSetting(spec: .Alignment, valueSize: UInt(sizeofValue(alignment)), value: &alignment)]
//let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, 1)
let paragraphStyle = CTParagraphStyleCreate(alignmentSetting, UInt(alignmentSetting.count))
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTParagraphStyleAttributeName, paragraphStyle)
// set font attribute
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTFontAttributeName, font);
// set color attribute
CFAttributedStringSetAttribute(attrStr,CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTForegroundColorAttributeName,textcolor.CGColor);
// Prepare the text using a Core Text Framesetter.
let framesetter = CTFramesetterCreateWithAttributedString(attrStr);
// Get the frame that will do the rendering.
let currentRange = CFRangeMake(0, 0);
let frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, nil);
// Put the text matrix into a known state. This ensures
// that no old scaling factors are left in place.
CGContextSetTextMatrix(myPDFContext, CGAffineTransformIdentity);
// Draw the frame.
CTFrameDraw(frameRef, myPDFContext);
}

Resources