I have took below website as reference, and tried to convert this to Swift code. Refer to “collectioViewLayout” part, I have difficulties to make things in order.
Appreciated that.UICollectionVIew circlesLayout
The original code is as below:-
- (CGSize)sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
if ([[self.collectionView.delegate class] conformsToProtocol:#protocol(UICollectionViewDelegateFlowLayout)]) {
return [(id)self.collectionView.delegate collectionView:self.collectionView layout:self sizeForItemAtIndexPath:indexPath];
}
return CGSizeMake(0, 0);}
My revised swift code as follows:-
func sizeForItem(at indexPath: IndexPath?) -> CGSize {
if type(of: collectionView.delegate) is UICollectionViewDelegateFlowLayout {
if let indexPath = indexPath {
return collectionView!.delegate?.collectionView!(collectionView!, layout: self, sizeForItemAt: indexPath) ?? CGSize.zero
}
return CGSize.zero
}
return CGSize(width: 0, height: 0)
}
However, some errors appeared. (see below)
Part 1) Cast from 'UICollectionViewDelegate?.Type' to unrelated type 'UICollectionViewDelegateFlowLayout' always fails
Part 2) Incorrect argument labels in call (have ':layout:sizeForItemAt:', expected':targetIndexPathForMoveFromItemAt:toProposedIndexPath:')
Is there anyone who can guide me how to fix those problem into correct swift code please?
The Objective-C code checks the type of collectionView.delegate, but in Swift you have to do a cast to tell the compiler that you are sure that it is indeed of type UICollectionViewDelegateFlowLayout{
func sizeForItem(at indexPath: IndexPath?) -> CGSize {
// "as?" does the cast, evaluating the expression to nil if it fails.
if let delegate = collectionView!.delegate as? UICollectionViewDelegateFlowLayout,
// you can combine these two checks into one if statement. Just separate them with ","
let indexPath = indexPath {
return delegate.collectionView?(collectionView, layout: self, sizeForItemAt: indexPath) ?? .zero
}
return .zero
}
Related
I'm trying to covert Speakrbox code in Objective-C.
I have already converted most of the code but I have a little problem with this one:
private func updateCallDurationForVisibleCells() {
/*
Modify all the visible cells directly, since -[UITableView reloadData] resets a lot
of things on the table view like selection & editing states
*/
let visibleCells = tableView.visibleCells as! [CallSummaryTableViewCell]
guard let indexPathsForVisibleRows = tableView.indexPathsForVisibleRows else { return }
for index in 0..<visibleCells.count {
let cell = visibleCells[index]
let indexPath = indexPathsForVisibleRows[index]
guard let call = call(at: indexPath) else { return }
cell.durationLabel?.text = durationLabelText(forCall: call)
}
}
I tried to convert it, and here is what I have:
-(void) updateCallDurationForVisibleCells :(id)sender {
/*
Modify all the visible cells directly, since -[UITableView reloadData] resets a lot
of things on the table view like selection & editing states
*/
_visibleCells = _tableview.visibleCells;
if(_indexPathsForVisibleRows == _tableview.indexPathsForVisibleRows) { return ; }
int index;
for (index=0; index<_visibleCells.count; index ++)
{
UICollectionViewCell *cell = _visibleCells[index];
NSIndexPath *indexPath = _indexPathsForVisibleRows[index];
UITableViewCell* call;
if((call = [self.tableView cellForRowAtIndexPath:indexPath]))
{ return ; }
}
}
Could any one please help me to convert this Swift code into Objective-C? The code doesn't compile for the reason that I didn't know how to convert this line of code:
cell.durationLabel?.text = durationLabelText(forCall: call)
Also, I don't know if I did it in the right way, especially the conversion of guard let.
you will find here Call and durationLabelText functions that I have used in pdateCallDurationForVisibleCells function:
private func call(at indexPath: IndexPath) -> SpeakerboxCall? {
return callManager?.calls[indexPath.row]
}//swift
private func durationLabelText(forCall call: SpeakerboxCall) -> String? {
return call.hasConnected ? callDurationFormatter.format(timeInterval: call.duration) : nil
}//Swift
Here's a fixed up version of your Objective-C code. Please note that when you see var or let in Swift, you are creating a local variable. Do not create a bunch a needless instance variables or properties in the Objective-C code when you should just use local variables.
- (void)updateCallDurationForVisibleCells {
/*
Modify all the visible cells directly, since -[UITableView reloadData] resets a lot
of things on the table view like selection & editing states
*/
NSArray *indexPathsForVisibleRows = self.tableView.indexPathsForVisibleRows;
if(!indexPathsForVisibleRows) { return ; }
NSArray *visibleCells = self.tableview.visibleCells;
for (NSInteger index = 0; index < _visibleCells.count; index++) {
CallSummaryTableViewCell *cell = visibleCells[index];
NSIndexPath *indexPath = indexPathsForVisibleRows[index];
SomeDataType *call = [self call:indexPath];
if (!call) { return; }
cell.durationLabel.text = [self durationLabelText:call];
}
}
There is some information missing which means this conversion may not be 100% correct.
You haven't shown the Objective-C signature of the call: method so I can't be sure how to call it properly and I don't know what data type to use for the call variable. Replace SomeDataType with the proper type.
You haven't shown the Objective-C signature of the durationLabelText: method so I can't be 100% sure about the proper way to call it.
If you post those details in your question I can be sure this answer is updated properly.
Edit: Thanks to users like #rmaddy, newbies like me get to learn how to write answers.Adding the final full code part.
Skip to the final part of the answer to get the working code.
In case you want to know where you went wrong, read the full answer.
There are multiple things that are possibly not right here, let me address what I know:
The guard statement is used for graceful exit. I'm no Java expert, but consider this to be like Exceptions in JAVA. The Obj-C code for your first guard would be:
if (nil == _tableview.indexPathsForVisibleRows){
return
}
This also means that there's no necessity for the _indexPathsForVisibleRows variable.
I'd recommend this article to understand guard better:
https://ericcerney.com/swift-guard-statement/
With respect to your second guard, please pay attention to detail.
call(at: indexPath) is not the same as cellForRowAtIndexPath.
call(at: indexPath) is a class function that returns a datatype, say CallType. cellForRowAtIndexPath is a UITableView function, that returns a cell.
The cell in the below line is more likely to be of type CallSummaryTableViewCell
UICollectionViewCell *cell = _visibleCells[index];
Regarding durationLabelText(), it seems to be a class function in the swift file whose declaration would be something like:
//Assume the type of "call" is CallType
private func durationLabelText(forCall call : CallType) -> String{
//your code here
}
The Objective-C equivalent would be:
-(NSString*) durationLabelTextForCall: (id<CallType>) call{
//your code here
}
The Objective-C call would be:
cell.durationLabel.text = [self durationLabelTextForCall: call]
So, the final code would be like this:
-(NSString*) durationLabelTextForCall: (id<CallType>) call{
//your code here
}
-(id<CallType>) callAtIndexPath:(NSIndexPath) indexPath{
//your code here
}
- (void)updateCallDurationForVisibleCells {
/*
Modify all the visible cells directly, since -[UITableView reloadData] resets a lot
of things on the table view like selection & editing states
*/
if (nil == _tableview.indexPathsForVisibleRows){
return
}
NSArray *visibleCells = self.tableview.visibleCells;
for (index=0; index<visibleCells.count; index ++)
{
CallSummaryTableViewCell *cell = visibleCells[index];
NSIndexPath *indexPath = _tableview.indexPathsForVisibleRows[index];
id<CallType> call = [self callAtIndexPath:indexPath];
if (nil == call){
return
}
cell.durationLabel.text = [self durationLabelTextForCall: call]
}
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I have a function which returns image if condition is satisfied else it returns nil
- (UIImage *)getProfilePic{
if (doc.userProperties != nil) {
UIImage *image = [UIImage imageNamed:#"placeholder.png"];
return image;
}
else {
return nil;
}
}
I want to convert this is in swift. I have tried this but it shows error while returning nil and also it crashes by showing error as unwrapping nil.
func getProfilePic(){
var image : UIImage?
if doc!.userProperties != nil {
image = UIImage(named: "placeholder.png")!
return image!
}
else {
return nil
}
}
at "return nil" line it shows nil is not compatible with return type ' uiimage'
func getProfilePic(){
var image : UIImage?
if doc!.userProperties != nil {
image = UIImage(named: "placeholder.png")!
return image!
}
else {
return nil
}
}
Right, so there are a few problems here. First, your code doesn't have a return type. If we ignore the body of your method and look just at the signature, the Objective-C equivalent would look like this:
- (void)getProfilePic;
So Swift & Objective-C would be complaining about the same thing here: what you're trying to return and the declared return type of the method do not match.
In case it's helpful since you seem perhaps more familiar with Objective-C than Swift, here's what your Swift method would look like if we translated it back into Objective-C:
- (void)getProfilePic {
UIImage *image;
if (doc.userProperties) {
image = UIImage(named: #"placeholder.png");
return image;
}
else {
return nil;
}
}
And again, this would generate the same or similar compile time warnings or errors, because the return type does not match the method signature. But Objective-C would not crash for unwrapping nil (but Swift will).
What you're actually trying to return is a UIImage?, so we need to update our method signature.
func getProfilePic() -> UIImage? {
if doc?.userProperties != nil {
return UIImage(named: "placeholder.png")
}
return nil
}
Assuming that userProperties holds perhaps a URL to an image you want to download or maybe the image itself, in the future we're going to want a slightly different construct... something more like this:
func getProfilePic() -> UIImage? {
guard let userProperties = doc?.userProperties else {
return nil
}
// extract the image from userProperties and return it
}
Try this code sample :
func getProfilePic() -> UIImage? {
let imageName = "placeholder.png"
var image: UIImage?
if (doc.userProperties != nil) {
image = UIImage(named: imageName)
}
return image
}
i can't seem to get this to work, i'm trying to implant a long press gesture on uicollectionview
found this code here in SO, now i just need to convert it to swift, so i can use it probely
obj-c
-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state != UIGestureRecognizerStateEnded) {
return;
}
CGPoint p = [gestureRecognizer locationInView:self.collectionView];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:p];
if (indexPath == nil){
NSLog(#"couldn't find index path");
} else {
// get the cell at indexPath (the one you long pressed)
UICollectionViewCell* cell =
[self.collectionView cellForItemAtIndexPath:indexPath];
// do stuff with the cell
}
}
my approach in swift
func handleLongPress(gestureRecognizer: UILongPressGestureRecognizer){
if (gestureRecognizer.state != UIGestureRecognizerState.Ended){
return
}
let p: CGPoint = gestureRecognizer.locationInView(self.collectionView)
let indexPath: NSIndexPath = self.collectionView.indexPathForItemAtPoint(p)!
if (indexPath == nil) { // Error here 'NSIndexPath' is not convertible to 'UIGestureRecognizerState'
println("couldn't find index path")
} else {
let cell: UICollectionViewCell = self.collectionView.cellForItemAtIndexPath(indexPath)
}
}
appreciate any help, it fails at if (indexPath == nil) with the error 'NSIndexPath' is not convertible to 'UIGestureRecognizerState'
Try to use optional binding to find out whether an optional contains a value. indexPathForItemAtPoint returns an optional value.
if let indexPath = self.collectionView.indexPathForItemAtPoint(p)
{
let cell: UICollectionViewCell = self.collectionView.cellForItemAtIndexPath(indexPath)
}
else
{
println("couldn't find index path")
}
Read about optionals in Apple's swift book.
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-ID309
You can also use optional chaining and map over the optional indexpath.
let cell = collectionView.indexPathForItemAtPoint(p).map {
self.collectionView.cellForItemAtIndexPath($0)
}
if let cell = cell {
// Do something with non-optional cell
} else {
println("No cell for point")
}
The cell variable will be a UICollectionViewCell? (optional) but that is the return type of the cellForItemAtIndexPath call anyway.
If you can continue with or return the optional cell you can skip the whole if let section especially if you don't need to print the error (or do if cell == nil { println("No cell for point") }
See this blogpost for some more ways to handle optionals: How I Handle Optionals In Swift
I am trying to use the "Autosave Expanded Items" feature. When I expand a group with its children and restart the application all children are collapsed again and I don't know why they won't stay expanded.
I'm using core data to store my source list items.
This is what I have done/set so far:
Checked "Autosave Expanded Items" in NSOutlineView (Source List)
Set a name for "Autosave"
dataSource and delegate outlets assigned to my controller
This is my implementation for outlineView:persistentObjectForItem and outlineView:itemForPersistentObject.
- (id)outlineView:(NSOutlineView *)anOutlineView itemForPersistentObject:(id)object
{
NSURL *objectURI = [[NSURL alloc] initWithString:(NSString *)object];
NSManagedObjectID *mObjectID = [_persistentStoreCoordinator managedObjectIDForURIRepresentation:objectURI];
NSManagedObject *item = [_managedObjectContext existingObjectWithID:mObjectID error:nil];
return item;
}
- (id)outlineView:(NSOutlineView *)anOutlineView persistentObjectForItem:(id)item
{
NSManagedObject *object = [item representedObject];
NSManagedObjectID *objectID = [object objectID];
return [[objectID URIRepresentation] absoluteString];
}
Any ideas? Thanks.
EDIT:
I have a clue! The problem is maybe that the tree controller has not prepared its content on time. The methods applicationDidFinishLaunching, outlineView:persistentObjectForItem etc. are being be executed before the data has loaded or rather the NSOutlineView hasn't finished initializing yet. Any ideas how to solve this?
I've had the problem that my implementation of -outlineView:itemForPersistentObject: was not called at all. It turns out that this method is called when either "autosaveExpandedItems" or "autosaveName" is set.
My solution was to set both properties in Code and NOT in InterfaceBuilder. When i set the properties after the delegate is assigned, the method gets called.
I got this to work - you need to return the corresponding tree node instead of "just" its represented object.
In itemForPersistentObject:, instead of return item; you need return [self itemForObject:item inNodes:[_treeController.arrangedObjects childNodes]];
with
- (id)itemForObject:(id)object inNodes:(NSArray *)nodes {
for (NSTreeNode *node in nodes) {
if ([node representedObject] == object)
return node;
id item = [self itemForObject:object inNodes:node.childNodes];
if (item)
return item;
}
return nil;
}
where _treeController is the NSTreeController instance that you use to populate the outline view.
Expanding on Karsten's solution:
The method -outlineView:itemForPersistentObject: gets called after doing what Karsten suggests, but ONLY if you also set the datasource before setting the delegate.
So if Karsten's answer doesn't seem to work, check where your datasource is set and adjust accordingly.
(wanted to write this as a comment but I'm not allowed due to my newbie status ...)
Swift 5 answer
Karsten is right, itemForPersistentObject must return a NSTreeNode.
Here is a Swift 5 version of the solution:
// This method should return a NSTreeNode object
func outlineView(_ outlineView: NSOutlineView, itemForPersistentObject object: Any) -> Any? {
guard let uriAsString = object as? String,
let uri = URL(string: uriAsString) else { return nil }
if let psc = self.managedObjectContext.persistentStoreCoordinator,
let moID = psc.managedObjectID(forURIRepresentation: uri),
let group = self.managedObjectContext.object(with: moID) as? MyGroupEntity,
let nodes = self.expensesTreeController.arrangedObjects.children {
return self.findNode(for: group, in: nodes)
}
return nil
}
/// Utility method to find the corresponding NSTreeNode for a given represented object
private func findNode(for object: NSManagedObject, in nodes: [NSTreeNode]) -> NSTreeNode? {
for treeNode in nodes {
if (treeNode.representedObject as? NSManagedObject) === object {
return treeNode
}
}
return nil
}
I never got this working.
This is my current way of doing it:
First, I added an attribute "isExpanded" and saved for each node the status in the database.
Second, I expand the nodes when my treeController has prepared its content.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[treeSectionController addObserver:self
forKeyPath:#"content"
options:0
context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == treeSectionController) {
NSArray *sectionArray = [[treeSectionController arrangedObjects] childNodes];
for (NSTreeNode *node in sectionArray) {
if([[node representedObject] isExpandedValue]) {
[outlinePilesView expandItem:node];
}
}
[treeSectionController removeObserver:self forKeyPath:#"content"];
}
}
Wow! 6 years later and this is still causing headaches.
I couldn't get this working initially, even with Karsten's helpful solution re setting autoSaveName & autosaveExpandedItems in code; itemForPersistentObject was still being called before the outlineView was populated. The solution for me, whilst not very elegant, was to set a delay of .5 seconds before setting autosaveExpandedItems & autoSaveName. The half second delay in my app is not noticeable. I used Vomi's code as well. Delegate and dataSource are set in IB bindings. Here's full solution:
override func viewDidLoad() {
super.viewDidLoad()
let _ = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { (timer) in
self.keywordsOutlineView.autosaveExpandedItems = true
self.keywordsOutlineView.autosaveName = "KeywordsOutlineView"
timer.invalidate()
}
}
func outlineView(_ outlineView: NSOutlineView, persistentObjectForItem item: Any?) -> Any? {
if let node = item as? NSTreeNode {
if let object = node.representedObject as? FTKeyword {
return object.objectID.uriRepresentation().absoluteString
}
}
return nil
}
// This method should return a NSTreeNode object
func outlineView(_ outlineView: NSOutlineView, itemForPersistentObject object: Any) -> Any? {
if outlineView == keywordsOutlineView {
guard let uriAsString = object as? String,
let uri = URL(string: uriAsString) else { return nil }
if let psc = self.managedObjectContext.persistentStoreCoordinator,
let moID = psc.managedObjectID(forURIRepresentation: uri),
let group = self.managedObjectContext.object(with: moID) as? FTKeyword,
let nodes = self.keywordsTreeController.arrangedObjects.children {
return self.findNode(for: group, in: nodes)
}
return nil
}
return nil
}
/// Utility method to find the corresponding NSTreeNode for a given represented object
private func findNode(for object: NSManagedObject, in nodes: [NSTreeNode]) -> NSTreeNode? {
for treeNode in nodes {
if (treeNode.representedObject as? NSManagedObject) === object {
return treeNode
}
}
return nil
}
I've been around this for a while.. I thought this should be an easy task, but it isn't =D
What I am trying to do, is to display the combobox's list when the user clicks the combobox but not specifically in the button.
Any Idea?
Thanks in advance!
This answer fits the title of the question, but not question itself. Omer wanted to touch a text field and have the box popup.
This solution shows the popup when the user enters text.
I found this answer on cocoabuilder from Jens Alfke. I reposted his code here. Thanks Jens.
original cocoabuilder post: (http://www.cocoabuilder.com/archive/cocoa)
#interface NSComboBox (MYExpansionAPI)
#property (getter=isExpanded) BOOL expanded;
#end
#implementation NSComboBox (MYExpansionAPI)
- (BOOL) isExpanded
{
id ax = NSAccessibilityUnignoredDescendant(self);
return [[ax accessibilityAttributeValue:
NSAccessibilityExpandedAttribute] boolValue];
}
- (void) setExpanded: (BOOL)expanded
{
id ax = NSAccessibilityUnignoredDescendant(self);
[ax accessibilitySetValue: [NSNumber numberWithBool: expanded]
forAttribute: NSAccessibilityExpandedAttribute];
}
I used this code in my controlTextDidChange: method.
- (void) controlTextDidChange:(NSNotification *) aNotification {
NSTextField *textField = [aNotification object];
NSString *value = [textField stringValue];
NSComboBox *box = [self comboBox];
if (value == nil || [value length] == 0) {
if ([box isExpanded]) { [box setExpanded:NO]; }
} else {
if (![box isExpanded]) { [box setExpanded:YES]; }
}
}
Returns true if the NSComboBox's list is expanded
comboBox.cell?.isAccessibilityExpanded() ?? false
Open the NSComboBox's list
comboBox.cell?.setAccessibilityExpanded(true)
Close the NSComboBox's list
comboBox.cell?.setAccessibilityExpanded(false)
Ref. jmoody’s answer.
You can use the following code line:
[(NSComboBoxCell*)self.acomboBox.cell performSelector:#selector(popUp:)];
Put
comboBoxCell.performSelector(Selector("popUp:"))
Into
override func controlTextDidChange(obj: NSNotification) {}
is what I ended up with. Thanks #Ahmed Lotfy
Here's the full code, it works for me on OSX 10.11
override func controlTextDidChange(obj: NSNotification) {
if let comboBoxCell = self.comboBox.cell as? NSComboBoxCell {
comboBoxCell.performSelector(Selector("popUp:"))
}
}
Thanks to jmoody and Jens Alfke mentioned above. Here is a SWIFT translation of the above solution.
import Cocoa
class CComboBoxEx: NSComboBox {
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
// Drawing code here.
}
func isExpanded() -> Bool{
if let ax:AnyObject? = NSAccessibilityUnignoredDescendant(self) {
if ax!.accessibilityAttributeValue(NSAccessibilityExpandedAttribute) != nil {
return true
}
}
return false
}
func setExpanded (bExpanded:Bool) {
if let ax:AnyObject? = NSAccessibilityUnignoredDescendant(self) {
ax!.accessibilitySetValue(NSNumber(bool: bExpanded), forAttribute: NSAccessibilityExpandedAttribute)
}
}
}
NSComboBox was not designed to work this way. Because the user may want to edit the text in the control, they'll need to be able to click it without unexpectedly popping up the choices.
You would need to subclass NSComboBoxCell and change this behavior ... but then you'd have a standard-looking control that does not behave in a standard way. If you're determined to do this, take a look at the open source version of NSComboBoxCell. The interesting methods appear to be -popUpForComboBoxCell: and friends.
Based on the other answers I wrote this solution (tested with Xcode 10.2.1, Swift 5). It uses the same ideas but it's a little shorter.
// Put this extension for NSComboBox somewhere in your project
import Cocoa
public extension NSComboBox {
var isExpanded: Bool{
set {
cell?.setAccessibilityExpanded(newValue)
}
get {
return cell?.isAccessibilityExpanded() ?? false
}
}
}
// Set your corresponding NSViewController as NSComboBoxDelegate
// in the storyboard and add this piece of code
// to expand the combobox when the user types
class MyViewController: NSViewController, NSComboBoxDelegate {
func controlTextDidChange(_ notification: Notification) {
guard let comboBox = notification.object as? NSComboBox else { return }
if comboBox.isExpanded == false {
comboBox.isExpanded = true
}
}
}