Setting kCTUnderlineStyleAttributeName for an NSMutableAttributedString in Swift - ios

The documentation for kCTUnderlineStyleAttributeName relays the following:
kCTUnderlineStyleAttributeName
The style of underlining, to be applied at render time, for the text to which this attribute applies. Value must be a CFNumber object. Default is kCTUnderlineStyleNone. Set a value of something other than kCTUnderlineStyleNone to draw an underline. In addition, the constants listed in CTUnderlineStyleModifiers can be used to modify the look of the underline. The underline color is determined by the text's foreground color.
The signature for the setAttributes function is as follows:
func setAttributes(attrs: [NSObject : AnyObject]?, range: NSRange)
The issue that I'm having is that the documentation seems to allude to the fact that CTUnderlineStyle.Single can (and should, in Swift) be used as a value for the kCTUnderlineStyleAttributeName key. However, the former is a struct, and as a result does not conform to the AnyObject protocol required by the dictionaries value type.
Any ideas?

I spend quite a few time on this.
The value needs to confront the AnyObject protocol.
Instead of using CTUnderlineStyle.Single use NSNumber like this:
NSNumber(int: CTUnderlineStyle.Single.rawValue)
Example:
attributedString.addAttribute(kCTUnderlineStyleAttributeName, value:NSNumber(int: CTUnderlineStyle.Single.rawValue), range:NSRange(location:0,length:attributedString.length));

in swift 3 worked for me this code
let attributes:[AnyHashable : Any] = [kCTUnderlineStyleAttributeName as AnyHashable:NSNumber(value:CTUnderlineStyle.single.rawValue)]

Related

UIPasteboard setItems: options API, UTType.plainText: swift usage

I'm trying to use the following API in Swift: UIPasteboard: setItems(_:options:)
, as suggested in this answer:
UIPasteboard.general.setItems([[kUTTypeUTF8PlainText as String: text]], options: [.expirationDate : Date(timeIntervalSinceNow: 10)])
However, it seems that the kUTTypeUTF8PlainText constant has been deprecated and I need to use the UniformTypeIdentifiers framework:
pasteboard.setItems([[String(UTType.plainText): string]],
options: [.expirationDate: Date(timeIntervalSinceNow: 120)])
However, the setItems: method accept only String type as the key, so I need to somehow convert the UTType.plainText to be of type String. How can I achieve this?
You should use the identifier property (also note the equivalent type is called utf8PlainText):
UTType.utf8PlainText.identifier
As the documentation says:
API that doesn’t use UTType uses a String or CFString to refer to a type by its identifier.
And UIPasteBoard is such an API.

Manual logged screen_view events sometimes have screen_name - (not set)

My project consist of obj-c and swift classes. I use Firebase 7.3.0.
I manually log screen_view event for my screens. I call this method in viewWillAppear or viewDidAppear like this:
#objc class MyAnalyticConstants: NSObject {
static let myScreenName = "AwesomeScreen"
}
class MyViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
MyAnalyticsClass().logScreenViewEvent(name: MyAnalyticConstants.myScreenName)
}
}
class MyAnalyticsClass {
func logScreenViewEvent(name: String, parameters: [String: Any]? = nil) {
var param = [String: Any]()
if let parameters = parameters {
param = parameters
}
param[AnalyticsParameterScreenName] = name
logEvent(name: AnalyticsEventScreenView, parameters: param)
}
func logEvent(name: String, parameters: [String: Any]?) {
analytics.logEvent(name, parameters: parameters)
}
}
I turned off automatic screenview reporting by setting FirebaseAutomaticScreenReportingEnabled to NO (Boolean) in the Info.plist. I use struct with static names for my screens.
However, sometimes I see (not set) value for "screen_view" event inside my google analytics path exploration for production. I can't catch this while using DebugView.
screenshot
I would really appreciate it, if somebody could help me to fix or explain it.
EDIT:
I swizzled firebase method
+ (void)xxx_logEventWithName:(NSString *)name
parameters:(nullable NSDictionary<NSString *, id> *)parameters {
[self xxx_logEventWithName:name parameters:parameters];
if ([name isEqualToString:#"screen_view"] && [parameters[#"screen_name"] length] <= 3) {
NSLog(#"%#", #[][1]);
}
and jumped through app during 30 min. I didn't catch up crash. Any other ideas?
Ok, there are a few things you could typically do in this case.
I wrote it as an afterthought, but it's something you should make sure of before doing technical debugging that follows: you should go to your analytics property/view and debug the filters. Maybe you have replacing filters interfering with your values, but I presume you checked your data in a full and unfiltered view where your app is the sole "stream" of data. This is important. The bug can come from a different app to this property, so either make sure you're the only source, or make sure you filter out other sources/apps/platforms.
Check your logEvent function calls where you either send AnalyticsEventScreenView or just the "screen_view" string as the first parameter. You see how the Firebase lib uses one function to send all kinds of events? They now treat screenviews as events. Which has its elegancy, but also may lead to unintended mistakes. Check what the globals actually mean in here: https://github.com/firebase/firebase-cpp-sdk/blob/0c8c8b29bc2d62d66c6ac49ff2c3fb04f815a687/analytics/ios_headers/FIREventNames.h
Check your logScreenViewEvent function calls. Pay attention to cases when you pass the first parameter as a variable. Also make sure you're never setting the AnalyticsParameterScreenName, which is also known as a string "screen_name" from here: https://github.com/firebase/firebase-cpp-sdk/blob/0c8c8b29bc2d62d66c6ac49ff2c3fb04f815a687/analytics/ios_headers/FIRParameterNames.h in the parameters dictionary, the second attribute. Cuz setting it there will effectively overwrite whatever is the first attribute you're setting. I actually usually suggest having only one argument for the screenview function declaration, especially to avoid collisions like this.
Oh, almost forgot. Make sure you ALWAYS use your neat MyAnalyticsClass wrapper and never call the native logEvent(). I would just check all files where you include the Firebase sdk and see if it should be replaces with the wrapper.
Finally, if the above didn't help, you can insert the check in both your function wrappers to throw an error whenever the event name equals to "screen_view" and the "screen_name" parameter's length not more than 2 character (I'm just trying to include all falsy values, so null, undefined, nil, whatever). And run unit tests or even better - regression testing with things set like that. Well, or manually test it out, watching for the errors in the console rather than the web debugger.

CoreText - how to center text within frame?

I am trying to center CoreText, and the best example I found was in this blog post. However, I was working with NSAttributedString, and although the CFMutableAttributedStringReference should be toll-free bridged to NSMutableAttributedString, this doesn't seem to work any bridging magic when I try to introduce the following in my drawRect method:
NSMutableAttributedString* attString = [[NSMutableAttributedString alloc]
initWithString:[NSString stringWithFormat:#"%d %#", self.currentNum, self.units]]; //2
// set paragraph style attribute
CFAttributedStringSetAttribute(attString, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTParagraphStyleAttributeName, paragraphStyle);
// set font attribute
CFAttributedStringSetAttribute(attString, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTFontAttributeName, font);
In particular, I am getting the same build error for both of the last two lines:
Incompatible pointer types passing retainable parameter of type 'NSMutableAttributedString'__strong to a CF function expecting 'CFMutableAttributedStringRef' (aka 'struct __CFAttributedString *') type.
I get that toll-free bridging doesn't have to go backwards, but then how can I set the alignment of my NSMutableAttributedString to center it in the provided frame? I don't know the text or size of the text in advance, so I need to be able to center it so it looks alright in a variety of sizes.
Thanks for any suggestions.
You have to cast toll free bridged types as documented in Casting and Object Lifetime Semantics.
Quote:
Through toll-free bridging, in a method where you see for example an NSLocale * parameter, you can pass a CFLocaleRef, and in a function where you see a CFLocaleRef parameter, you can pass an NSLocale instance. You also have to provide other information for the compiler: first, you have to cast one type to the other; in addition, you may have to indicate the object lifetime semantics.
Example:
CFAttributedStringSetAttribute((CFMutableAttributedStringRef)attString, …
Alternatively you can use NSMutableAttributedString's methods to add attributes.

PHAssetCollectionChangeRequest: addAssets() now accepts NSFastEnumeration. how to achieve this?

My Application tries to import an image from photos and adds to an album.
But in latest swift changes. addAssets() accepts parameter as NSFastEnumeration. So I get an error as shown in the image.
even the Apple API document has this same code: https://developer.apple.com/library/prerelease/ios/documentation/Photos/Reference/PHAssetChangeRequest_Class/index.html#//apple_ref/occ/instp/PHAssetChangeRequest/placeholderForCreatedAsset
What is the alternative or how do I addAssets now?
So, I did some research, and according to NSHipster, NSEnumeration is a protocol implemented by NSArray, NSSet, and NSDictionary. This suggests that if you convert [assetPlaceholder] to an NSArray, you'll be able to use it in the method. And, in fact, this compiles:
let enumeration: NSArray = [assetPlaceholder!]
albumChangeRequest!.addAssets(enumeration)

Swift - which types to use? NSString or String

With the introduction of Swift I've been trying to get my head round the new language
I'm an iOS developer and would use types such as NSString, NSInteger, NSDictionary in an application. I've noticed that in the "The Swift Programming Language" ebook by Apple, they use the Swift types String, Int, Dictionary
I've noticed the Swift types don't have (or are differently named) some of the functions that the Foundation types do. For example NSString has a length property. But I've not been able to find a similar one for the Swift String.
I'm wondering, for an iOS application should I still be using the Foundation types?
You should use the Swift native types whenever possible. The language is optimized to use them, and most of the functionality is bridged between the native types and the Foundation types.
While String and NSString are mostly interchangeable, i.e, you can pass String variables into methods that take NSString parameters and vice versa, some methods seem to not be automatically bridged as of this moment. See this answer for a discussion on how to get the a String's length and this answer for a discussion on using containsString() to check for substrings. (Disclaimer: I'm the author for both of these answers)
I haven't fully explored other data types, but I assume some version of what was stated above will also hold true for Array/NSArray, Dictionary/NSDictionary, and the various number types in Swift and NSNumber
Whenever you need to use one of the Foundation types, you can either use them to type variables/constants explicitly, as in var str: NSString = "An NSString" or use bridgeToObjectiveC() on an existing variable/constant of a Swift type, as in str.bridgeToObjectiveC().length for example. You can also cast a String to an NSString by using str as NSString.
However, the necessity for these techniques to explicitly use the Foundation types, or at least some of them, may be obsolete in the future, since from what is stated in the language reference, the String/NSString bridge, for example, should be completely seamless.
For a thorough discussion on the subject, refer to Using Swift with Cocoa and Objective-C: Working with Cocoa Data Types
NSString : Creates objects that resides in heap and always passed by reference.
String: Its a value type whenever we pass it , its passed by value.
like Struct and Enum, String itself a Struct in Swift.
public struct String {
// string implementation
}
But copy is not created when you pass. It creates copy when you first mutate it.
String is automatically bridged to Objective-C as NSString. If the Swift Standard Library does not have, you need import the Foundation framework to get access to methods defined by NSString.
Swift String is very powerful it has plethora of inbuilt functions.
Initialisation on String:
var emptyString = "" // Empty (Mutable)
let anotherString = String() // empty String immutable
let a = String(false) // from boolean: "false"
let d = String(5.999) // " Double "5.99"
let e = String(555) // " Int "555"
// New in Swift 4.2
let hexString = String(278, radix: 18, uppercase: true) // "F8"
create String from repeating values:
let repeatingString = String(repeating:"123", count:2) // "123123"
In Swift 4 -> Strings Are Collection Of Characters:
Now String is capable of performing all operations which anyone can
perform on Collection type.
For more information please refer apple documents.
Your best bet is to use Swift native types and classes, as some others have noted NSString has toll free translation to String, however, they're not the same a 100%, take for example the following
var nsstring: NSString = "\U0001F496"
var string: String = "\U0001F496"
nsstring.length
count(string)
you need to use the method count() to count the characters in string, also note that nsstring.length returns 2, because it counts its length based on UTF16.
Similar, YES
The same, NO
String and NSString are interchangeable, so it doesn't really matter which one you use. You can always cast between the two, using
let s = "hello" as NSString
or even
let s: NSString = "hello"
NSInteger is just an alias for an int or a long (depending on the architecture), so I'd just use Int.
NSDictionary is a different matter, since Dictionary is a completely separate implementation.
In general I'd stick to swift types whenever possibile and you can always convert between the two at need, using the bridgeToObjectiveC() method provided by swift classes.
Swift 4 update
String gets revisions in swift 4. Now you can directly call count on it and it consider grapheme clusters as 1 piece, like an emoji. NSString is not updated and is counting it in another way.
var nsstring: NSString = "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦"
var string: String = "πŸ‘©β€πŸ‘©β€πŸ‘§β€πŸ‘¦"
print(nsstring.length) // 11
print(string.count) // 1
Since the objective C types are still dynamically dispatched they're probably going to be slower. I'd say you're best served using the Swift native types unless you need to interact with objective-c APIs
Use the Swift native types whenever you can. In the case of String, however, you have "seamless" access to all the NSString methods like this:
var greeting = "Hello!"
var len = (greeting as NSString).length
Swift Strings are quite elegant and easy to use, unless you need to parse them. The whole concept of indexing into Swift strings is just plain crazy. Whenever I need to parse through a typical unicode string, I convert it to NSString. Swift makes this a bit tricky though in that the common "character" integer expressions like ' ' or 'A' or '0' don't work in Swift. You have to use 32, 65, 48. Unfortunately, I'm not kidding! Because of this, I've put most of my string parsing code into an NSString extension written in Objective-C.
Yes I do know WHY Swift's designers made String indexing so crazy: They wanted to be able to express many-byte characters like emoji as single "Characters". My choice would have been to let this rare use case be expressed as multiple UTF16 characters, but what the heck.
String is a struct
// in Swift Module
public struct String
{
}
NSString is a class
// in Foundation Module
open class NSString : NSObject
{
}

Resources