Setting an object's delegate multiple times - delegates

If I do object.delegate = self; then in that object's implementation do self.delegate = object;
Does that rewrite the object's delegate or does it assign it multiple delegates?

If you do object.property = 1, then do object.property = 2, is property equal to both 1 and 2?
It'll rewrite the delegate.

Related

How to assign one class type instance to another class type reference in swift to apply dynamic polymorphism

iam unable to set backgroundcolor for label at runtime
func example() {
let lbl: UILabel = UILabel()
let arrTemp: NSMutableArray = NSMutableArray()
arrTemp.addObject(lbl)
let tempButton: UIButton? = arrTemp.objectAtIndex(0) as? UIButton
tempButton.backgroundColor = UIColor.whiteColor()
}
Your logic is wrong for several reasons.
Let's see what you are doing
You are adding a UILabel to arrTemp.
arrTemp.addObject(lbl)
Now arrTemp does contain UILabel as only element
Next you retrieve the first element of arrTemp and try to cast it as UIButton.
let tempButton: UIButton? = arrTemp.objectAtIndex(0) as? UIButton
Of course the conditional cast will always fail (given the current code) and tempButton will be populated with nil.
You are accessing a property of tempButton (which is an Optional Value) without unwrapping it. The Swift compiled does not allow you to do this.
tempButton.backgroundColor = UIColor.whiteColor()
You could update the last line to make the compiler happy
tempButton?.backgroundColor = UIColor.whiteColor()
but the last assignment will never be executed since tempButton will always be nil.
Now a question: what are you trying to do? What do you want to achieve putting a UILabel into an array and then trying to use that label as a UIButton?
You can only reference the object as something it actually is. Otherwise, you could ask a label for its buttonType by using your untyped array without getting a compile-time error.
If you don't want that form of type safety, Objective-C is probably a better choice.
A UILabel is a subclass of UIView; a UIView has a settable property of backgroundColor. So, to set a UILabel color use simply:
func example() {
let lbl: UILabel = UILabel()
lbl.backgroundColor = UIColor.whiteColor()
// do something with lbl.
}

Custom Setter in NSManagedObject Subclass

I have a entitiy in my NSMAnagedObject that is depended on other enititys, so after reading the documention on Depend Keys I come up with the following within my subclass
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:#"assetAmount"]) {
NSArray *affectingKeys = #[#"assetAlternativeCur", #"assetAltCur", #"assetCurrency"];
keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
}
return keyPaths;
}
- (void)setAssetAmount:(NSDecimalNumber *)assetAmount
{
[self willChangeValueForKey:#"assetAmount"];
if ([[self useAlternativeCur] boolValue] == YES) {
NSDecimalNumber *result;
result = [[self assetConversionRate] decimalNumberByMultiplyingBy:[self assetAlternativeCur]];
[self setPrimitiveAssetAmount:result];
} else {
[self setPrimitiveAssetAmount:assetAmount];
}
[self didChangeValueForKey:#"assetAmount"];
}
My problem is the setter "setAssetAmount" only gets called when I change the "assetAmount" value directly, if a change the values included in the keyPathsForValuesAffectingValueForKey the setter does not get called. Am I going about this the wrong way? I expected the setter to get called each time any value changes.
If you have observers of the value of assetAmount, or bindings to the value of assetAmount, those observers and bindings would be updated appropriately when the values affecting assetAmount changed. The assetAmount getter is then invoked.
In the assetAmount getter, you can then recalculate the assetAmount that you want to return.
If you want to do the calculation every time the getter is called, you're done.
If you want to save the value of the calculation in the ivar, then you have to make sure to access the ivar directly to avoid KVO and bindings triggering. (Was a catch-22)
If you do not want the getter to be calculating the value every time, I believe you can just call assetAmount's setter from the setters for the other values. You wouldn't even need the valuesAffecting stuff, because you'd be calling the setter and triggering KVO.
In that sense, you'd need keyPathsForValuesAffectingValueForKey: only if you want your assetAmount getter to do the calculation every time. If you want it saved in the ivar, just use its setter when the other values change.
(Also, you can implement keyPathsForValuesAffectingAssetAmount if you were to go that route)
NSArray *affectingKeys = #[#"assetAlternativeCur", #"assetAltCur", #"assetCurrency"];
tells the runtime system that these 3 other things affect the value for assetAmount... so when one of those changes you will get a notification that assetAmount changed, but your setters for the other 3 keys don't change the way that the getter for assetAmount works.. so you don't get any change...
you can force this a couple of different ways.. your assetAmount getter could be dynamic and just calculate the value then return it.
or in your setters for the other 3 keys you could do something like:
self.assetAmount = self.primativeAssetAmount;
but I would consider that sloppy and will probably cause problems down the road... you should just have one value and then another method that is the adjustedAssetAmount that has the formatted version...
an example would be temperature.
where you have a property absoluteTemp, and getters for C and F.
getter for C: => absoluteTemp+273.15
getter for F: => 9/5 * absoluteTemp + 32

Using delegates in OS X

I'm new at OS X development, I've been having a problem getting a delegate callback and I somehow suspect that it might be a memory problem. I have an NSViewController. In it's init method I am setting up a custom NSObject as so:
MyObject *aManager = [[MyObject alloc] initManager];
__theManager = aManager;
self.theManager.delegate = self;
[aManager release];
the delegate I've setup as nonatomic, assign. Looking at the breakpoints I should be seeing the callback in my view controller but this never happens. Any ideas?
__theManager = aManager; should be self.theManager = aManager;, assuming theManager is a retained property. The problem you have is that alloc] init]; gives aManager a retain count of +1. __theManager = aManager; does not increase that count, as the iVar is set directly. When you release it, the retain count becomes 0, and so it is deallocated.

performSelector:withObject:afterDelay: - a way to do this to pass something other than an object?

I am trying to use this method to show a second modal after a first modal has been dismissed... The method itself works fine, except that it requires me to pass an object as a parameter and what I really want to do is pass a UIImageControllerSourceType instead. Is there another method I should be using to do this or is there an easy way to make UIImageControllerSourceType an object? I feel like this should not be too hard but I have been struggling with it for awhile now...
- (void)showModalTwoImageSearchViewControllerWithSourceType:(UIImagePickerControllerSourceType *)sourceType {
if (self.modalViewController) {
[self performSelector:#selector(showModalTwoImageSearchViewControllerWithSourceType:)
withObject:sourceType
afterDelay:0.1f];
return;
}
// present second modal view - remembering to deal with cases of camera or photo library
if (sourceType == UIImagePickerControllerSourceTypePhotoLibrary) {
//do one thing
}
// lets assume it was UIImagePickerControllerSourceTypeCamera
else {
// do something else
}
}
UIImagePickerControllerSourceType is ultimately a NSUInteger and the method you're trying to do (the performSelector one) only takes Objective C objects in its withObject parameter and not C types or pointers.
You can pass NSUIntegers by creating a NSNumber object and stuffing your integer into that.
To create a NSNumber object, you can create one as easily as NSNumber's numberWithUnsignedInteger method.
UIImagePickerControllerSourceType is an enum. the easiest way to use it as an object is to make it an NSNumber:
NSNumber *n = [NSNumber numberWithInt:sourceType];

Dynamically hiding columns in a NSTableView

I want to dynamically hide/show some of the columns in a NSTableView, based on the data that is going to be displayed - basically, if a column is empty I'd like the column to be hidden. I'm currently populating the table with a controller class as the delegate for the table.
Any ideas? I see that I can set the column hidden in Interface Builder, however there doesn't seem to be a good time to go through the columns and check if they are empty or not, since there doesn't seem to be a method that is called before/after all of the data in the table is populated.
In Mac OS X v10.5 and later, there is the setHidden: selector for NSTableColumn.
This allows columns to be dynamically hidden / shown with the use of identifiers:
NSInteger colIdx;
NSTableColumn* col;
colIdx = [myTable columnWithIdentifier:#"columnIdent"];
col = [myTable.tableColumns objectAtIndex:colIdx];
[col setHidden:YES];
I've done this with bindings, but setting them up programmatically instead of through Interface Builder.
This psuedo-snippet should give you the gist of it:
NSTableColumn *aColumn = [[NSTableColumn alloc] initWithIdentifier:attr];
[aColumn setWidth:DEFAULTCOLWIDTH];
[aColumn setMinWidth:MINCOLWIDTH];
[[aColumn headerCell] setStringValue:columnLabel];
[aColumn bind:#"value"
toObject:arrayController
withKeyPath:keyPath
options:nil];
[tableView addTableColumn:aColumn];
[aColumn release];
Of course you can add formatters and all that stuff also.
It does not work in the Interface Builder. However it works programatically. Here is how I bind a NSTableViewColumn with the identifier "Status" to a key in my NSUserDefaults:
Swift:
tableView.tableColumnWithIdentifier("Status")?.bind("hidden", toObject: NSUserDefaults.standardUserDefaults(), withKeyPath: "TableColumnStatus", options: nil)
Objective-C:
[[self.tableView tableColumnWithIdentifier:#"Status"] bind:#"hidden" toObject:[NSUserDefaults standardUserDefaults] withKeyPath:#"TableColumnStatus" options:nil];
I don't have a complete answer at this time, but look into Bindings. It's generally possible to do all sorts of things with Cocoa Bindings.
There's no Visibility binding for NSTableColumn, but you may be able to set the width to 0.
Then you can bind it to the Null Placeholder, and set this value to 0 - but don't forget to set the other Placeholders to reasonable values.
(As I said, this is just a start, it might need some tweaking).
A NSTable is just the class that paints the table. As you said yourself, you have some class you give the table as delegate and this class feeds the table with the data to display. If you store the table data as NSArray's within your delegate class, it should be easy to find out if one column is empty, isn't it? And NSArray asks your class via delegate method how many columns there are, so when you are asked, why not looking for how many columns you have data and report that number instead of the real number of columns you store internally and then when being asked for providing the data for (column,row), just skip the empty column.
There is no one time all the data is populated. NSTableView does not store data, it dynamically asks for it from its data source (or bound-to objects if you're using bindings). It just draws using data it gets from the data source and ditches it. You shouldn't see the table ask for data for anything that isn't visible, for example.
It sounds like you're using a datasource? When the data changes, it's your responsibility to call -reloadData on the table, which is a bit of a misnomer. It's more like 'invalidate everything'.
That is, you should already know when the data changes. That's the point at which you can compute what columns should be hidden.
#amrox - If I am understanding your suggestion correctly, you're saying that I should bind a value to the hidden property of the NSTableColumns in my table? That seems like it would work, however I don't think that NSTableColumn has a hidden property, since the isHidden and setHidden messages control the visibility of the column - which tells me that this isn't a property, unless I'm missing something (which is quite possible).
I would like to post my solution updated for Swift 4 using Cocoa bindings and the actual isHidden flag without touching the column widths (as you might need to restore the original value afterwards...). Suppose we have a Checkbox to toggle some column visibility (or you can always toggle the hideColumnsFlag variable in the example below in any other way you like):
class ViewController: NSViewController {
// define the boolean binding variable to hide the columns and use its name as keypath
#objc dynamic var hideColumnsFlag = true
// Referring the column(s)
// Method 1: creating IBOutlet(s) for the column(s): just ctrl-drag each column here to add it
#IBOutlet weak var hideableTableColumn: NSTableColumn!
// add as many column outlets as you need...
// or, if you prefer working with columns' string keypaths
// Method 2: use just the table view IBOutlet and its column identifiers (you **must** anyway set the latter identifiers manually via IB for each column)
#IBOutlet weak var theTableView: NSTableView! // this line could be actually removed if using the first method on this example, but in a real case, you will probably need it anyway.
// MARK: View Controller Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
// Method 1
// referring the columns by using the outlets as such:
hideableTableColumn.bind(.hidden, to: self, withKeyPath: "hideColumnsFlag", options: nil)
// repeat for each column outlet.
// Method 2
// or if you need/prefer to use the column identifiers strings then:
// theTableView.tableColumn(withIdentifier: .init("columnName"))?.bind(.hidden, to: self, withKeyPath: "hideColumnsFlag", options: nil)
// repeat for each column identifier you have set.
// obviously use just one method by commenting/uncommenting one or the other.
}
// MARK: Actions
// this is the checkBox action method, just toggling the boolean variable bound to the columns in the viewDidLoad method.
#IBAction func hideColumnsCheckboxAction(_ sender: NSButton) {
hideColumnsFlag = sender.state == .on
}
}
As you may have noticed, there is no way yet to bind the Hidden flag in Interface Builder as on XCode10: you can see the Enabled or Editable bindings, but only programmatically you will have access to the isHidden flag for the column, as it is called in Swift.
As noted in comments, the second method relies on the column identifiers you must manually set either via Interface Builder on the Identity field after selecting the relevant columns or, if you have an array of column names, you can enumerate the table columns and assign the identifiers as well as the bindings instead of repeating similar code lines.
I found a straightforward solution for it.
If you want to hide any column with the Cocoa binding technology:
In your instance of the NSArrayController, create an attribute/parameter/slot/keyed value which will have NSNumber 0 if you want a particular column to be hidden and any value if not.
Bind the table column object's maxWidth parameter to the data slot, described in (1). We will use the maxWidth bound parameter as a message receiver.
Subclass the NSTableColumn:
import Cocoa
class Column: NSTableColumn {
/// Observe the binding messages
override func setValue(_ value: Any?, forKey key: String) {
if key == "maxWidth" && value != nil { // Filters the signal
let w = value as! NSNumber // Explores change
if w == NSNumber(integerLiteral: 0) {
self.isHidden = true
} else {
self.isHidden = false
}
return // No propagation for the value change
}
super.setValue(value, forKey: key) // Propagate the signal
}
}
Change the class of the column to Column.

Resources