Return type of UIImage in Swift [closed] - objective-c

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
}

Related

How to transfer Object c to Swift aout collectionVIewLayout

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
}

From Swift to Objective C - updateCallDurationForVisibleCells

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]
}
}

How to return a custom object in a swift convenience initializer?

I'm trying to do something like this:
public extension UIImage {
public convenience init(whatever: Int) {
UIGraphicsBeginImageContextWithOptions(...)
//...
let image = UIGraphicsGetImageFromCurrentContext()
UIGraphicsEndImageContext()
return image // <- impossible
}
}
But this is not possible as "nil" is the only valid return for an initializer... How do i do this?
For example, the Objtive-C method [UIImage imageNamed:] is a class method (that can return whatever it wants in Objective-C) and it was mapped to the swift initializer UIImage(named:).
What you want is a class factory method, not an initializer. Most factory methods in Foundation/Cocoa are automatically bridged to initializers, but if what you want can't be done via init, you can add a new class method:
public extension UIImage {
class func imageWithWhatever(whatever: Int) -> UIImage {
UIGraphicsBeginImageContextWithOptions(...)
//...
let image = UIGraphicsGetImageFromCurrentContext()
UIGraphicsEndImageContext()
return image
}
}
This is because you are returning a new object, not self. The point of init is to create the structure of your object, not a new one, so if you want to do it as a convenience init, you need to do it like this:
public extension UIImage {
public convenience init?(whatever: Int) {
defer {
UIGraphicsEndImageContext()
}
UIGraphicsBeginImageContextWithOptions(...)
//...
guard let currentContext = UIGraphicsGetCurrentContext() else { return nil }
guard let image = currentContext.makeImage() else { return nil }
self.init(cgImage:image)
}
}
perhaps instead of a convenience init, you want to create a class function that is doing what you are asking:
public class func createImage(whatever: Int) -> UIImage? {
defer {
UIGraphicsEndImageContext()
}
UIGraphicsBeginImageContextWithOptions(...)
//...
guard let currentContext = UIGraphicsGetCurrentContext() else { return nil }
guard let cgImage = currentContext.makeImage() else { return nil }
let image = UIImage(cgImage: cgImage)
return image
}
I apologize that this is not 100% to code, but that is basically the gist of it

How to programmatically open an NSComboBox's list?

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
}
}
}

How do I take a "screenshot" of an NSView?

I need to take the contents of an NSView and put them in an NSImage, for an experimental project. Is this possible? I did some Googling, tried two methods that I found - but they didn't really work. Any suggestions?
From WWDC 2012 Session 245 (translated to Swift):
let viewToCapture = self.window!.contentView!
let rep = viewToCapture.bitmapImageRepForCachingDisplay(in: viewToCapture.bounds)!
viewToCapture.cacheDisplay(in: viewToCapture.bounds, to: rep)
let img = NSImage(size: viewToCapture.bounds.size)
img.addRepresentation(rep)
[[NSImage alloc] initWithData:[view dataWithPDFInsideRect:[view bounds]]];
let dataOfView = view.dataWithPDFInsideRect(view.bounds)
let imageOfView = NSImage(data: dataOfView)
NSView.bitmapImageRepForCachingDisplay() (mentioned in this answer) doesn't render the colors correctly on some views.
CGWindowListCreateImage() works perfectly for me.
Here's my implementation:
extension NSView {
#objc func takeScreenshot() -> NSImage? {
let screenRect = self.rectInQuartzScreenCoordinates()
guard let window = self.window else {
assert(false); return nil
}
let windowID = CGWindowID(window.windowNumber)
guard let screenshot = CGWindowListCreateImage(screenRect, .optionIncludingWindow, windowID, []) else {
assert(false); return nil
}
return NSImage(cgImage: screenshot, size: self.frame.size)
}
}
This code uses a method NSView.rectInQuartzScreenCoordinates(). To implement it you'll first have to convert the bounds of your view to screenCoordinates using NSView and NSWindow methods and then you need to flip the coordinates like this.