I'm trying to create an attributed string with a strikethrough, however, this simple task seems to be harder to figure out than I anticipated. Here is what I have currently (which is not working). Thanks for the help!
NSAttributedString *theTitle = [[[NSAttributedString alloc] initWithString:#"strikethrough text" attributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSColor whiteColor], NSForegroundColorAttributeName, NSUnderlinePatternSolid, NSStrikethroughStyleAttributeName, nil]] autorelease];
First, the value for NSStrikethroughStyleAttributeName must be an NSNumber, not a simple integer. Second, I think you have to include NSUnderlineStyleSingle:
...:[NSDictionary dictionaryWithObjectsAndKeys:
...,
[NSNumber numberWithInteger:NSUnderlinePatternSolid | NSUnderlineStyleSingle],
NSStrikethroughStyleAttributeName,
nil]...
You can also simply use:
NSAttributedString *theAttributedString;
theAttributedString = [[NSAttributedString alloc] initWithString:theString
attributes:#{NSStrikethroughStyleAttributeName:
[NSNumber numberWithInteger:NSUnderlineStyleSingle]}];
Update :
Swift 2.0 version
let theAttributedString = NSAttributedString(string: theString, attributes: [NSStrikethroughColorAttributeName: NSUnderlineStyle.StyleSingle])
func addAttributes(attrs: [String : AnyObject], range: NSRange)
NSUnderlineStyleAttributeName:
The value of this attribute is an NSNumber object containing an integer.
Since the NSUnderlineStyle enum's rawValue is Int Type, you should initialize a NSNumber Object with it
Swift2.1:
attrStr.addAttributes([NSStrikethroughStyleAttributeName: NSNumber(integer: NSUnderlineStyle.StyleSingle.rawValue)], range: NSMakeRange(x, y))
x is the location, the start of your text
y is the length of the text
Swift 4:
NSAttributedString(string: "test string", attributes: [.strikethroughStyle: NSUnderlineStyle.styleSingle.rawValue])
Swift 5:
NSAttributedString(string: "test string", attributes: [.strikethroughStyle: NSUnderlineStyle.single.rawValue])
Mind the fact that .strikethroughStyle and .underlineStyle expect an integer value (specifically an NSNumber), therefore we're using NSUnderlineStyle's .rawValue in the examples.
(Setting NSUnderlineStyle causes unrecogognized selector exception)
Related
I wish to filter an array using a predicate but I'm being curved balled with the correct handling of data types.
First off, I have a list I want to filter with:
NSMutableArray *HospitalIDs = [[NSMutableArray alloc]init];
[HospitalIDs addObject: #"1"]; // Must be "id", int & NSInteger not accepted by XCode
[HospitalIDs addObject: #"2"]; // Must be "id", int & NSInteger not accepted by XCode
[HospitalIDs addObject: #"3"]; // Must be "id", int & NSInteger not accepted by XCode
Secondly, I have a list of objects that I want to filter using the array above:
(It's defined as NSMutableArray *HospitalObjects and is pre-populated with HospitalObjects)
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.HospitalID IN %#", HospitalIDs];
[HospitalObjects filterUsingPredicate:predicate];
NSLog(#"%i",[HospitalObjects count]);
//The expected result is 3, but it prints 0.
WHY?
Because by first array contains NSString values, but my HospitalObject.HospitalID is of type int.
I don't really want to change the data type of HospitalID in my object definition to NSString, but I cannot filter on integer values, what do I do?
Can I convert the data type WITHIN the predicate syntax? How?
First of all, the best way to store numbers in NSArray is to use NSNumber objects. You can simply convert a NSInteger to NSNumber using Objective-C literal syntax:
[HospitalIDs addObject: #(1)];
[HospitalIDs addObject: #(2)];
[HospitalIDs addObject: #(3)];
Then your predicate should work without any further changes.
There is a Type NSNumber that wraps ints, floats, bools, .. in a object.
NSMutableArray * hospitalIDs = [[NSMutableArray alloc] init];
[hospitalIDs addObject: #1]; // You can wrap an normal int into an NSNumber jusing #. In some siztuations you have to use #(yourOrdinaryValueTypeVariable)
[hospitalIDs addObject: #2];
[hospitalIDs addObject: #3];
By the way try to use proper naming using CamelCase:
Classes start with a upper case: SomeClassName
Objects, variables and method names with lower case: `SomeClassName* someObject;
Constants are all upper case: MY_CONSTANT
I am trying to implement searching in NSTextView with search query coming from my custom NSSearchField.
Sounds pretty simple, but I cannot get it working.
So far I've looked through all the Apple Documentation about NSTextFinder, its client and FindBarContainer. The TextFinder simply provides the FindBarView to the container, and container shows it when you activate searching.
All the communication between the client, container and TextFinder is hidden. It just looks like a black-box that is designed to work "as is" without any customisation or interference.
But what about - (void)performAction:(NSTextFinderAction)op method of NSTextFinder? Isn't it for sending custom commands to the TextFinder?
I was trying to assign a new search string to it with the following:
NSPasteboard* pBoard = [NSPasteboard pasteboardWithName:NSFindPboard];
[pBoard declareTypes:[NSArray arrayWithObjects:NSPasteboardTypeString, NSPasteboardTypeTextFinderOptions, nil] owner:nil];
[pBoard setString:_theView.searchField.stringValue forType:NSStringPboardType];
NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSTextFinderCaseInsensitiveKey,
[NSNumber numberWithInteger:NSTextFinderMatchingTypeContains], NSTextFinderMatchingTypeKey,
nil];
[pBoard setPropertyList:options forType:NSPasteboardTypeTextFinderOptions];
[textFinder performAction:NSTextFinderActionSetSearchString];
but that doesn't work and simply breaks the normal findBar operation.
I have a strong feeling that I am doing something wrong.
All I want is to have a standard search functionality in my own NSSearchField. Is that possible?
I bet I am not the first one who is not happy with normal findBar.
Your help is very needed and appreciated!
For an NSTextView, NSTextFinder is mostly just a user interface for NSString's func range(of searchString: String, options mask: NSString.CompareOptions = [], range rangeOfReceiverToSearch: NSRange) -> NSRange
If you want to implement your own search on an NSTextView, use that. To search forward, you construct a range starting at the end of the current selections's range and going to the end of the NSTextView's text. To search backward, construct a range starting at 0 and going to the beginning of the current selection's range, and tell NSString to use backwards search.
If NSString returns a .notFound range, implement wrap-around yourself.
If you need startsWith, endsWith or wholeWord you'll need to take the result NSString's func gives you, check to see if it will do, and if not adjust the range and call it again.
You can use NSComboBox. Return search value using below delegate:
- (NSString *)comboBox:(NSComboBox *)aComboBox completedString:(NSString *)substring
{
if ([aComboBox tag] == 101 || [aComboBox tag] == 102) {
NSArray *currentList;
if ([aComboBox tag] == 101) {
NSArray *keyArray = keySuggestions;
currentList = keyArray;
} else {
currentList = [NSArray arrayWithArray:self.valueSuggestions];
}
NSEnumerator *theEnum = [currentList objectEnumerator];
id eachString;
NSInteger maxLength = 0;
NSString *bestMatch = #"";
while (nil != (eachString = [theEnum nextObject])) {
NSString *commonPrefix = [eachString
commonPrefixWithString:substring options:NSCaseInsensitiveSearch];
if ([commonPrefix length] >= [substring length] && [commonPrefix
length] > maxLength)
{
maxLength = [commonPrefix length];
bestMatch = eachString;
break;
}
}
return bestMatch;
}
return substring;
}
-[NSMutableAttributedString initWithHTML:documentAttributes:] seems to mangle special characters:
NSString *html = #"“Hello” World"; // notice the smart quotes
NSData *htmlData = [html dataUsingEncoding:NSUTF8StringEncoding];
NSMutableAttributedString *as = [[NSMutableAttributedString alloc] initWithHTML:htmlData documentAttributes:nil];
NSLog(#"%#", as);
That prints “Hello†World followed by some RTF commands. In my application, I convert the attributed string to RTF and display it in an NSTextView, but the characters are corrupted there, too.
According to the documentation, the default encoding is UTF-8, but I tried being explicit and the result is the same:
NSDictionary *attributes = #{NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]};
NSMutableAttributedString *as = [[NSMutableAttributedString alloc] initWithHTML:htmlData documentAttributes:&attributes];
Use [html dataUsingEncoding:NSUnicodeStringEncoding] when creating the NSData and set the matching encoding option when you parse the HTML into an attributed string:
The documentation for NSCharacterEncodingDocumentAttribute is slightly confusing:
NSNumber, containing an int specifying the NSStringEncoding for the
file; for reading and writing plain text files and writing HTML;
default for plain text is the default encoding; default for HTML is
UTF-8.
So, you code should be:
NSString *html = #"“Hello” World";
NSData *htmlData = [html dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *options = #{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: #(NSUTF8StringEncoding)};
NSMutableAttributedString *as =
[[NSMutableAttributedString alloc] initWithHTML:htmlData
options: options
documentAttributes:nil];
The previous answer here works, but mostly by accident.
Making an NSData with NSUnicodeStringEncoding will tend to work, because that constant is an alias for NSUTF16StringEncoding, and UTF-16 is pretty easy for the system to identify. Easier than UTF-8, which apparently was being identified as some other superset of ASCII (it looks like NSWindowsCP1252StringEncoding in your case, probably because it's one of the few ASCII-based encodings with mappings for 0x8_ and 0x9_).
That answer is mistaken in quoting the documentation for NSCharacterEncodingDocumentAttribute, because "attributes" are what you get out of -initWithHTML. That's why it's NSDictionary ** and not just NSDictionary *. You can pass in a pointer to an NSDictionary *, and you'll get out keys like TopMargin/BottomMargin/LeftMargin/RightMargin, PaperSize, DocumentType, UTI, etc. Any values you try to pass in through the "attributes" dictionary are ignored.
You need to use "options" for passing values in, and the relevant option key is NSTextEncodingNameDocumentOption, which has no documented default value. It's passing the bytes to WebKit for parsing, so if you don't specify an encoding, presumably you're getting WebKit's encoding-guessing heuristics.
To guarantee the encoding types match between your NSData and NSAttributedString, what you should do is something like:
NSString *html = #"“Hello” World";
NSData *htmlData = [html dataUsingEncoding:NSUTF8StringEncoding];
NSMutableAttributedString *as =
[[NSMutableAttributedString alloc] initWithHTML:htmlData
options:#{NSTextEncodingNameDocumentOption: #"UTF-8"}
documentAttributes:nil];
Swift version of accepted answer is:
let htmlString: String = "Hello world contains html</br>"
let data: Data = Data(htmlString.utf8)
let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
]
let attributedString = try? NSAttributedString(data: data,
options: options,
documentAttributes: nil)
Im making an iOS app to do with currency. My app receives the value of maybe: $4. This value the app receives is put into an NSNumber. The trouble is the value actualy has a $ in it. How do I trim out the $ in the NSNumber? Or would I be better of putting it into an NSString?
Use NSNumberFormatter:
// set up your number formatter
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
// get a string that you'll be converting to a NSNumber
NSString *myNumberString = [NSString stringWithFormat:#"$4"]
// convert then print to the console
NSNumber *myNumber = [numberFormatter numberFromString:myNumberString];
NSLog(#"myNumber: %#", myNumber);
This should accomplish what you're looking to do. myNumberString will need to be altered to contain whatever string you're receiving.
NSNumberFormatter Documentation: https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSNumberFormatter_Class/Reference/Reference.html
Im trying to set an array for a timer program im writing in xcode. the values are in seconds, and what i want is to have a button in the interface builder that starts a timer with that number of seconds. This is the struct im trying to declare to provide the times in a .h header file. its just an array with 2 arrays in it, that i could call with #collegeTimes.constructive or something similar.
Thanks in advance!
- (NSDictionary *)debateTimes;
id debateTimes = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSDictionary dictionaryWithObjectsAndKeys:
#"540", #"constructive",
#"360", #"rebuttal",
#"180", #"cx",
#"600", #"prep",
nil], #"collegeTimes",
[NSDictionary dictionaryWithObjectsAndKeys:
#"480", #"constructive",
#"300", #"rebuttal",
#"180", #"cx",
#"480", #"prep",
nil], #"hsTimes",
nil]; \\error is called here.
This is the struct im trying to declare to provide the times in a .h header file
This is the problem. You can not create constant NSDictionary objects (or most other NS objects, for that matter) outside of a function. One way to do what wou want would be as follows:
SomeThing.h
#interface SomeThing : NSObject
{
...
}
+ (NSDictionary *)debateTimes;
#end
SomeThing.m
static NSDictionary * staticDebateTimes = nil;
#implementation SomeThing
...
+ (NSDictionary *)debateTimes
{
if (staticDebateTimes == nil)
{
staticDebateTimes = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSDictionary dictionaryWithObjects:...
}
return staticDebateTimes;
}
#end
This code would now be used externally as follows:
NSDictionary * debateTimes = [SomeThing debateTimes];
You cannot assign an objective-c object to a variable outside of a function. When a variable is assigned outside a function, its value becomes part of the executable. Since the value of the pointer to a object is not known until runtime, you cannot assign the object until it is created. (Constant NSStrings are an exception to this as they are also part of the executable)
The best way to store a structure like this would be to use an array of c structures.
typedef struct {
char *name;
NSTimeInterval constructive;
NSTimeInterval rebuttal;
NSTimeInterval cx;
NSTimeInterval prep;
} DebateTime;
DebateTime[2] = {{"collegeTimes", 540, 360, 180, 600},
{"hsTimes", 480, 300, 180, 480}};
You can also change the name and time intervals to constant strings if you wish.