diff --git a/2013-08-19-nshashtable-and-nsmaptable.md b/2013-08-19-nshashtable-and-nsmaptable.md index 1bc9e9d1..6aba6254 100644 --- a/2013-08-19-nshashtable-and-nsmaptable.md +++ b/2013-08-19-nshashtable-and-nsmaptable.md @@ -5,7 +5,8 @@ category: Cocoa tags: nshipster excerpt: "NSSet and NSDictionary, along with NSArray are the workhorse collection classes of Foundation. Unlike other standard libraries, implementation details are hidden from developers, allowing them to write simple code and trust that it will be (reasonably) performant." status: - swift: t.b.c. + swift: 2.0 + reviewed: September 11, 2015 --- `NSSet` and `NSDictionary`, along with `NSArray` are the workhorse collection classes of Foundation. Unlike [ other standard libraries](http://en.wikipedia.org/wiki/Java_collections_framework), implementation details are [hidden](http://ridiculousfish.com/blog/posts/array.html) from developers, allowing them to write simple code and trust that it will be (reasonably) performant. @@ -28,6 +29,15 @@ So without further ado, here's everything you need to know about two of the more ### Usage +~~~{swift} +let hashTable = NSHashTable(options: .CopyIn) +hashTable.addObject("foo") +hashTable.addObject("bar") +hashTable.addObject(42) +hashTable.removeObject("bar") +print(hashTable.allObjects) +~~~ + ~~~{objective-c} NSHashTable *hashTable = [NSHashTable hashTableWithOptions:NSPointerFunctionsCopyIn]; [hashTable addObject:@"foo"]; @@ -59,6 +69,13 @@ NSLog(@"Members: %@", [hashTable allObjects]); Instances where one might use `NSMapTable` include non-copyable keys and storing weak references to keyed delegates or another kind of weak object. +~~~{swift} +let delegate: AnyObject = ... +let mapTable = NSMapTable(keyOptions: .StrongMemory, valueOptions: .WeakMemory) +mapTable.setObject(delegate, forKey: "foo") +print("Keys: \(mapTable.keyEnumerator().allObjects)") +~~~ + ~~~{objective-c} id delegate = ...; NSMapTable *mapTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory @@ -80,17 +97,31 @@ Equal to `NSPointerFunctionsObjectPointerPersonality`. `NSMapTable` doesn't implement [object subscripting](http://nshipster.com/object-subscripting/), but it can be trivially added in a category. `NSDictionary`'s `NSCopying` requirement for keys belongs to `NSDictionary` alone: +~~~{swift} +extension NSMapTable { + subscript(key: AnyObject?) -> AnyObject? { + get { + return objectForKey(key) + } + + set { + setObject(newValue, forKey: key) + } + } +} +~~~ + ~~~{objective-c} @implementation NSMapTable (NSHipsterSubscripting) - (id)objectForKeyedSubscript:(id)key { - return [self objectForKey:key]; + return [self objectForKey:key]; } - (void)setObject:(id)obj forKeyedSubscript:(id)key { - [self setObject:obj forKey:key]; + [self setObject:obj forKey:key]; } @end diff --git a/2014-03-31-avspeechsynthesizer.md b/2014-03-31-avspeechsynthesizer.md index bdf235d3..9ab3fa75 100644 --- a/2014-03-31-avspeechsynthesizer.md +++ b/2014-03-31-avspeechsynthesizer.md @@ -4,7 +4,8 @@ author: Mattt Thompson category: Cocoa excerpt: "Though we're a long way off from Hal or Her, we should never forget about the billions of other people out there for us to talk to." status: - swift: 1.1 + swift: 2.0 + reviewed: September 10, 2015 --- Though we're a long way off from [_Hal_](https://www.youtube.com/watch?v=ARJ8cAGm6JE) or [_Her_](https://www.youtube.com/watch?v=WzV6mXIOVl4), we should never forget about the billions of other people out there for us to talk to. @@ -104,13 +105,13 @@ var utteranceLabel: UILabel! // MARK: AVSpeechSynthesizerDelegate -func speechSynthesizer(synthesizer: AVSpeechSynthesizer!, willSpeakRangeOfSpeechString characterRange: NSRange, utterance: AVSpeechUtterance!) { +func speechSynthesizer(synthesizer: AVSpeechSynthesizer, willSpeakRangeOfSpeechString characterRange: NSRange, utterance: AVSpeechUtterance) { let mutableAttributedString = NSMutableAttributedString(string: utterance.speechString) mutableAttributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: characterRange) utteranceLabel.attributedText = mutableAttributedString } -func speechSynthesizer(synthesizer: AVSpeechSynthesizer!, didFinishSpeechUtterance utterance: AVSpeechUtterance!) { +func speechSynthesizer(synthesizer: AVSpeechSynthesizer, didFinishSpeechUtterance utterance: AVSpeechUtterance) { utteranceLabel.attributedText = NSAttributedString(string: utterance.speechString) } ``` diff --git a/2014-11-03-uisplitviewcontroller.md b/2014-11-03-uisplitviewcontroller.md index efb97a8f..2c4ef0a2 100644 --- a/2014-11-03-uisplitviewcontroller.md +++ b/2014-11-03-uisplitviewcontroller.md @@ -4,7 +4,8 @@ author: Natasha Murashev category: Cocoa excerpt: "The introduction of iPhone 6+ brought on a new importance for UISplitViewController. With just a few little tweaks, an app can now become Universal, with Apple handling most of the UI logic for all the different screen sizes." status: - swift: 1.0 + swift: 2.0 + reviewed: September 11, 2015 --- The introduction of iPhone 6+ brought on a new importance for `UISplitViewController`. With just a few little tweaks, an app can now become Universal, with Apple handling most of the UI logic for all the different screen sizes. @@ -71,7 +72,7 @@ Even when the navigation controller is in place, the UI is not that much better The simplest way to fix this issue would be to somehow indicate that there is more to the app than what's currently on-screen. Luckily, the UISplitViewController has a **displayModeButtonItem**, which can be added to the navigation bar: -```swift +~~~{swift} override func viewDidLoad() { super.viewDidLoad() @@ -80,7 +81,18 @@ override func viewDidLoad() { navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem() navigationItem.leftItemsSupplementBackButton = true } -``` +~~~ + +~~~{objective-c} +- (void)viewDidLoad { + [super viewDidLoad]; + + // ... + + self.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem; + self.navigationItem.leftItemsSupplementBackButton = YES; +} +~~~ Build and Run on the iPad again, and now the user gets a nice indication of how to get at the rest of the app: @@ -98,7 +110,7 @@ There is one more optimization we can do for the iPhone 6+ via [`UISplitViewCont When the user first launches the app, we can make the master view controller fully displayed until the user selects a list item: -```swift +~~~{swift} import UIKit class SelectColorTableViewController: UITableViewController, UISplitViewControllerDelegate { @@ -122,11 +134,53 @@ class SelectColorTableViewController: UITableViewController, UISplitViewControll // MARK: - UISplitViewControllerDelegate - func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController: UIViewController!, ontoPrimaryViewController primaryViewController: UIViewController!) -> Bool { + func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController: UIViewController, ontoPrimaryViewController primaryViewController: UIViewController) -> Bool { return collapseDetailViewController } } -``` +~~~ + +~~~{objective-c} +@import UIKit; + +@interface SelectColorTableViewController : UITableViewController +@end + +@interface SelectColorTableViewController () + +@property (nonatomic) BOOL shouldCollapseDetailViewController; + +@end + +@implementation SelectColorTableViewController + +#pragma mark - UITableViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + // ... + + self.shouldCollapseDetailViewController = true; + self.splitViewController.delegate = self; +} + +// ... + +#pragma mark - UITableViewDelegate + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + self.shouldCollapseDetailViewController = false; +} + +#pragma mark - UISplitViewControllerDelegate + +- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController { + return self.shouldCollapseDetailViewController; +} + +@end +~~~ When the user first opens up the app on iPhone 6+ in portrait orientation, `SelectColorViewController` gets displayed as the primary view controller. Once the user selects a color or the app goes into the background, the `SelectColorViewController` gets collapsed again, and the `ColorViewController` is displayed: