I'm trying to convert a Objective-C app to Swift but I'm stuck with setBounds. The original code looks like this:
- (void)setBounds:(CGRect)bounds {
[super setBounds:bounds];
// code
}
What is the equivalent in Swift? I've tried looking in the documents and google but can't seem to figure it out.
You need to override bounds:
override var bounds: CGRect{
didSet{
//your code here
}
}
Related
I need to make init for my view controller in swift. so how can I make similar init method in swift.
- (instancetype)initWithFormlyData:(NSDictionary *)json afterUpdatingProfile:(void (^)(BOOL, BMErrors *))completion
{
self = [super initWithFormlyData:json];
if (self) {
self.updateCompletion = completion;
}
return self;
}
Here's one solution:
init(formlyData json: NSDictionary, afterUpdatingProfile completion: ((Bool, BMErrors?)->Void)?)
{
}
I took the liberty of making the completion's BMErrors an optional, even though your ObjC didn't specify nullable. You may have intended other parameters to be nullable too, so add optionals as needed.
You might want to just simplify to:
init(json: NSDictionary, completion: ((Bool, BMErrors?)->Void)?)
{
}
And maybe consider switching to a Swift Dictionary if your caller doesn't mind.
Hope this helps!
I wonder what's the Swift equivalent in calling a method on id in which the availability of the method is determined at runtime. Specifically I'm looking to do this pattern in Swift:
-(IBAction) handleEvent:(id) sender {
BOOL didDisable = NO;
if([sender respondsToSelector:#selector(setEnabled:)]) {
[sender setEnabled:NO];
didDisable = YES;
}
[self doSomethingAsyncWithCompletionHandler:^{
if(didDisable) {
[sender setEnabled:YES];
}
}];
}
The biggest problem is that setEnabled: is imported in Swift as a property (e.g. UIBarItem) and none of the following constructs compile
func handleEvent(sender: AnyObject) {
// Error: AnyObject does not have a member named "enabled"
sender.enabled? = false
// Error: (BooleanLiteralCompatible) -> _ is not identical to Bool
sender.setEnabled?(false)
}
You can in fact do it exactly the same way you were doing it before: by calling respondsToSelector:. Indeed, that is exactly what your proposed expression does:
sender.setEnabled?(false)
That expression is actually a shorthand - it calls respondsToSelector: first, and then calls setEnabled: only if the respondsToSelector: test passes. Unfortunately, as you say, you can't get that code to compile. That, however, is merely a quirk of Swift's known repertory of available methods. The fact is that, although it is a little tricky to get it to compile, it can be done - and once you get it to compile, it behaves just as you would expect.
However, I'm not going to explain how to make it compile, because I don't want to encourage this kind of trickery. This sort of dynamic messaging is discouraged in Swift. In general, dynamic messaging tricks such as key-value coding, introspection, and so forth are not needed in Swift and are not consonant with Swift's strong typing approach. It would be better to do things the Swift way, by casting optionally to something that you have reason to believe this thing might be and that has an enabled property. For example:
#IBAction func doButton(sender: AnyObject) {
switch sender {
case let c as UIControl: c.enabled = false
case let b as UIBarItem: b.enabled = false
default:break
}
}
Or:
#IBAction func doButton(sender: AnyObject) {
(sender as? UIControl)?.enabled = false
(sender as? UIBarItem)?.enabled = false
}
In Swift 2.0 beta 4, your prayers are answered; this code becomes legal:
#IBAction
func handleEvent(sender: AnyObject) {
if sender.respondsToSelector("setHidden:") {
sender.performSelector("setHidden:", withObject: true)
}
}
If you want to avoid using the respondsToSelector: method you could define a protocol instead. Then extend the classes you want to use that is already in conformance with this protocol's definition (enabled) and define the function with a generic variable conforming to your protocol.
protocol Enablable{
var enabled:Bool { get set }
}
extension UIButton : Enablable {}
extension UIBarButtonItem : Enablable {}
//....
func handleEvent<T:Enablable>(var sender: T) {
sender.enabled = false
}
If you need to use it with an IBAction method a little bit of a work around is required since you cannot use generics directly on them.
#IBAction func handleEventPressed(sender:AnyObject){
handleEvent(sender);
}
We also need a matching generic function without Enablable conformance so that we can call handleEvent without knowing wether or not sender is Enablable. Luckily the compiler is smart enough to figure out which of the two generic functions to use.
func handleEvent<T>(var sender: T) {
//Do Nothing case if T does not conform to Enablable
}
As a workaround/alternative, you can use Key-Value Coding:
#IBAction func handler(sender: AnyObject) {
if sender.respondsToSelector("setEnabled:") {
sender.setValue(false, forKey:"enabled")
}
}
This works with both Swift 1.2 (Xcode 6.4) and Swift 2.0 (Xcode 7 beta).
So I need to do this simple line of code in my game. It is currently in swift and I am trying to convert it to Objective-C and I got stuck as in Objective C there is no
sprite.frame.cointains(//CGPOINT);
I am wondering how can I go ahead and do this as it is vital for my game.
Here's the code in swift
if fruitNode.frame.contains(location!) {
touchPoint = location!
touching = true
}
If anybody can help me to respond that will be highly appreciated!
Thanks!
In Objective C you could do this using SKNode method:
if ([fruitNode containsPoint:location])
{
// do this
}
or if you needed to use CGRect for some reason :
if (CGRectContainsPoint(fruitNode.frame, location))
{
// do this
}
definitely check out the Apple reference for CGRectContainsPoint - https://developer.apple.com/library/prerelease/ios/documentation/GraphicsImaging/Reference/CGGeometry/index.html#//apple_ref/c/func/CGRectContainsPoint
I'm porting an OS X app which was using WebView to using WKWebView, the new "modern WebKit API" introduced in OS X Yosemite. My previous WebView subclass supported dropping files onto it by first calling [self registerForDraggedTypes:#[NSFilenamesPboardType]] and then simply implementing - (BOOL)performDragOperation:(id < NSDraggingInfo >)sender.
This doesn't work with the new WKWebView, as performDragOperation never gets called, nor do any of the NSDraggingDestination protocol methods that I tried.
I also tried making a parent NSView implement the protocol, and I'm still not getting the messages. Removing the WKWebView from the hierarchy makes the parent NSView receive those messages.
I also tried implementing the WKNavigationDelegate protocol to prevent the default drop behaviour of WKWebView to happen and this didn't change a thing either.
Edit: Upon further inspection (suggested by Scott Kyle / #appden on twitter), a private class WKView that implements the NSDraggingDestination protocol is a subview of the WKWebView. My code should likely try to get the dragging notifications before the WKView sees them and acts on them.
The only solution I found, thanks to Scott Kyle, was to replace the performDragOperation: method defined on WKView with my own implementation using object_getClass, class_getInstanceMethod and method_exchangeImplementations from the Objective-C runtime. Hopefully, in the future, the WKUIDelegate protocol will be extended to support custom hooks into the drag-and-drop protocol as implemented privately by WKWebView.
Here's an example showing how we exchange the implementation of the nested view's performDragOperation: with our own from our WKWebView subclass:
// Override the performDragOperation: method implemented on WKView so that we may get drop notification.
var originalMethod = class_getInstanceMethod(object_getClass(subviews[0]), "performDragOperation:")
var overridingMethod = class_getInstanceMethod(object_getClass(self), "performDragOperation:")
method_exchangeImplementations(originalMethod, overridingMethod)
And then the implementation where we delegate to a dropDelegate object.
override func performDragOperation(sender: NSDraggingInfo) -> Bool {
let myWebView = superview as MyWebView
if let dropDelegate = myWebView.dropDelegate {
return dropDelegate.webView(myWebView, performDragOperation: sender)
}
return false
}
I needed to retain WKWebView's handling of drags, but be able to introspect and modify pasteboards before it received them. Subclassing WKView is not really helpful since all of WKWebView's classes would still use the original, so I used the runtime swizzle proposed earlier + a class extension.
import WebKit
import Foundation
import ObjectiveC
extension WKView {
func shimmedPerformDragOperation(sender: NSDraggingInfo) -> Bool {
var pboard = sender.draggingPasteboard()
if let items = pboard.pasteboardItems {
for item in items {
if let types = item.types? {
for type in types {
if let value = item.stringForType(type.description) {
NSLog("DnD type(\(type)): \(value)")
}
}
}
}
}
return self.shimmedPerformDragOperation(sender) //return pre-swizzled method
}
}
var webview = WKWebView(frame: CGRectZero, configuration: WkWebViewConfiguration())
var wkview = (webview.subviews.first as WKView) // 1 per frame?
var origDnD = class_getInstanceMethod(WKView.self, "performDragOperation:")
var newDnD = class_getInstanceMethod(WKView.self, "shimmedPerformDragOperation:")
method_exchangeImplementations(origDnD, newDnD)
You do need to add a small snippet to your bridging header to extend WKView:
#import WebKit;
#interface WKView : NSView <NSTextInputClient> {
}
- (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo;
#end
I'd like to override UILabel's setText method but I'm not sure that:
A) it's possible, and
B) if maybe there's a better way to achieve what I'm trying to accomplish.
I have a subclass of UIView that has a UILabel property as one of its sub-views. I'd like to know when the UILabel's "text" property changes so I can adjust the size of the rounded rect behind it. If I owned UILabel I'd just override setText...but UILabel is Apple's and its implementation is hidden.
So, how should I be going about this?
Subclasses of UILabel can override the setText method quite easily. I'm not really sure why this hasn't yet been included as a legitimate answer on this 4 year old question.
- (void) setText:(NSString *)text
{
[super setText:text];
[self sizeToFit];
}
You can use Key-Value Observing to track changes to the UILabel.text property. The approach involves three steps:
1) Registering to observe the property, when you load the view
[label addObserver:inspector
forKeyPath:#"text"
options:(NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionOld)
context:NULL];
2) Receiving a notification about any changes:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([keyPath isEqual:#"text"]) {
// put your logic here
}
// be sure to call the super implementation
// if the superclass implements it
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
3) De-registering the observation whenever you aren't interested any more:
[label removeObserver:inspector forKeyPath:#"text"];
Matt answer is good for Objective-C, but doesn't work in Swift (normal, it didn't existed when he answered), the accepted answer from notnoop does work in swift, even though it is more complicated, just to give another idea in swift you can use the didSet:
class MyLabel: UILabel {
override var text: String? {
didSet {
if let text = text {
println("the new text is: \(text)")
} else {
println("the text has been set to nil")
}
}
}
Based on Drix answer, I think this is a more correct approach (using set instead of didSet):
class UnreadCountLabel: UILabel {
override var text: String? {
set {
if let newValue = newValue where !newValue.isEmpty {
super.text = " \(newValue) "
self.hidden = false
} else {
super.text = newValue
self.hidden = true
}
}
get {
return super.text
}
}
}
Are you just using a rounded rectangle as the background for the Label? If that is the case, you can look into using UIIMage stretchableImageWithLeftCapWidth:topCapHeight. This will take an image you've created that has a left and top repeating section with a width you specify and automatically stretch it to your width.
If not, Key-Value observing is the way to go. Just to cover another option--this is like "playing with fire," as Apple programmer Evan Doll said in one of his Stanford lectures--you can use method swizzling to exchange one method implementation for another.
void method_exchangeImplementations(Method m1, Method m2);
In this case, you want to tweak the implementation of setText, but you also want to call the original setText in UILabel. So you could exchange setText with setTextAndUpdateSize, and inside setTextAndUpdateSize do what setText does originally plus add on a little more. If you are confused or think this is a bad idea, it probably is. You can get a Method object to pass into method_exchangeImplementations by calling class_getInstanceMethod([NSSTring class], #selector (methodName).
Once your method swizzle has been called once, inside your new method you can then call the old implementation of setText from within the new one by using, yes, setTextAndUpdateSize. It's confusing and not recommended, but it works. A good example can be found in the developer sample code.
I pulled of Method Swizzling in Swift 2.0. Changing the font of the entire application by swapping the implementation of setText method of the label.
Copy the code in app delegate and use the customSetText to make application level changes
// MARK: - Method Swizzling
extension UILabel {
public override class func initialize() {
struct Static {
static var token: dispatch_once_t = 0
}
// make sure this isn't a subclass
if self !== UILabel.self {
return
}
dispatch_once(&Static.token) {
let originalSelector = Selector("setText:")
let swizzledSelector = Selector("customSetText:")
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}
// MARK: - Custom set text method for UI Label
func customSetText(text: String) {
self.customSetText(text)
//set custom font to all the labels maintaining the size UILabel
self.font = UIFont(name: "Lato-LightItalic", size: self.font.pointSize)
}
}