SwiftUI: Inline image size - swiftui

I am adding inline image (SF symbol) into Text with .appendInterpolation like this:
Text("Hello \(Image(systemName: "swift"))")
This works great, but I would like to change the symbol size. How can I do it?
I tried
Text("Hello \(Image(systemName: "swift").font(.system(size: 12)))")
but I only get this error: 'appendInterpolation' is unavailable: Unsupported type for interpolation, see LocalizedStringKey.StringInterpolation for supported types.

How about using plus operator for Text?
Text("Hello ")
+
Text(Image(systemName: "swift"))
.font(.title.bold())
+
Text(" Swift!")

Related

How can I load an Image to Image() from a valid path or URL from Bundle in SwiftUI?

I have a PNG file in my Bundle.main and I want use that URL or path to feed my Image in SwiftUI, right now I am using this down code, but I like to use just SwiftUI not UIKit mixed
Image(uiImage: UIImage(contentsOfFile: path)!)
Do we have some initializing way for Image that accept path or url?
If you want to init from a filepath you could create an extension on Image that handles the boilerplate code for you. But you need to handle the case if the UIImage doesn't exist as the Image(uiImage:) expects a non-optional UIImage as a parameter.
Something like this should work:
extension Image {
init(contentsOfFile: String) {
// You could force unwrap here if you are 100% sure the image exists
// but it is better to handle it gracefully
if let image = UIImage(contentsOfFile: contentsOfFile) {
self.init(uiImage: image)
} else {
// You need to handle the option if the image doesn't exist at the file path
// let's just initialize with a SF Symbol as that will exist
// you could pass a default name or otherwise if you like
self.init(systemName: "xmark.octagon")
}
}
}
You would then use it like this:
Image(contentsOfFile: "path/to/image/here")
Or you could use the property of UIImage to load from the bundle
extension Image {
init(uiImageNamed: String) {
if let image = UIImage(named: uiImageNamed, in: .main, compatibleWith: nil) {
self.init(uiImage: image)
} else {
// You need to handle the option if the image doesn't exist at the file path
self.init(systemName: "xmark.octagon")
}
}
}
You would then use it like this:
Image(uiImageNamed: "ImageName")

CAAnimation is deprecated in iOS 11 - need a recent answer?

Ok, I have looked EVERYWHERE and am trying here to simply extract an animation from a .dae imported into Xcode (or a .scn, whatever) so that I can run it on the model throughout my scene kit game. the problem is every way to do this that is referenced (including here Scenekit: Add animation to SCNNode from external Collada)
seems to be deprecated in iOS 11. So I have no way to animate any 3D model I put in my scene.
Right now I am able to display the model just still with this - I get the model from my dae and put it into my blank scn :
if let d = modelScene.rootNode.childNodes.first
{
theDude.node = d
theDude.setupNode() //this scales it down
}
func setupNode()
{
node.scale = SCNVector3(x: modifier, y: modifier, z: modifier)
}
//Then add to scene on tap
let clone = theDude.node.clone()
theDude.node = clone
self.sceneView.scene.rootNode.addChildNode(theDude.node)
theDude.node.position = hitPosition
This works. Trying to get it to run the animation that I added to it in Maya, however, does not. I added these extensions per Apple's example to my project:
https://developer.apple.com/library/content/samplecode/Fox/Listings/Swift_Common_SceneKitExtensions_swift.html
And was trying to do this as it says to, just with a cube as a basic test (from the above question):
func addAnim()
{
let characterScene = SCNScene(named: "art.scnassets/cubeAnimatedSkeleton.dae")!
let characterTopLevelNode = characterScene.rootNode.childNodes[0]
sceneView.scene.rootNode.addChildNode(characterTopLevelNode)
let idleAnimation = CAAnimation.animationWithSceneNamed("art.scnassets/cubeAnimatedSkeleton.dae")!
idleAnimation.usesSceneTimeBase = false
idleAnimation.repeatCount = Float.infinity
characterTopLevelNode.addAnimation(idleAnimation, forKey: "idle")
}
But with this CAAnimation.animationWithSceneNamed("art.scnassets/cubeAnimatedSkeleton.dae")! the whole thing does not work because this extension from APPLE:
extension CAAnimation {
class func animationWithSceneNamed(_ name: String) -> CAAnimation? {
var animation: CAAnimation?
if let scene = SCNScene(named: name) {
scene.rootNode.enumerateChildNodes({ (child, stop) in
if child.animationKeys.count > 0 {
animation = child.animation(forKey: child.animationKeys.first!) //ERROR
stop.initialize(to: true)
}
})
}
return animation
}
}
says that addAnimation for key was deprecated in iOS 11. Replacing that with animation = child.animationPlayer(forKey: child.animationKeys.first!) doesn't work, and I don't know what else to do.
What is wrong here?

Override open variable for class in Swift and SpriteKit for use in XCode's ActionEditor

I'm trying to make small platformer game, just in learning purposes. I've tried to animate character with array of SKTextures like this:
let animation: SKAction = SKAction.animate(
with: frames.map {
let texture : SKTexture = SKTexture(imageNamed: $0)
texture.filteringMode = SKTextureFilteringMode.nearest
return texture
},
timePerFrame: 0.15
)
frames variable is array of SKTextures. I've used map to set filtering mode for each texture. This works fine and dandy, but than i've discovered that i can create animations (actions) in ActionEditor in XCode with AnimateWithTextures action. And here lies my problem. This is much more efficient, but i can't change filtering mode for textures in animation in ActionEditor. I've looked into SKTexture class and saw this:
open class SKTexture : NSObject, NSCopying, NSCoding {
...
/**
The filtering mode the texture should use when not drawn at native size. Defaults to SKTextureFilteringLinear.
*/
open var filteringMode: SKTextureFilteringMode
...
}
If i'm not mistaken open means that i can override variable, but i don't know how. I want to set default value of filteringMode to SKTextureFilteringMode.nearest for all SKTextures so ActionEditor will use textures with texture filtering set to nearest neighbor.
Also if you know how to set filtering mode for textures in specific action – i would like to know how to do it too. Thanks
you can create a function to setup the animation for you, and create an extension to SKTexture with your custom init, and when your ready just call it to your node.
extension SKTexture { // Declare this extension outside your class decleration
convenience init(customImageNamed: String) {
self.init(imageNamed: customImageNamed)
self.filteringMode = .nearest
}
}
func setupAnimationWithPrefix(_ prefix: String, start: Int, end: Int, timePerFrame: TimeInterval) -> SKAction{
var textures = [SKTexture]()
for i in start...end{
textures.append(SKTexture(customImageNamed: "\(prefix)\(i)"))
}
return SKAction.animate(with: textures, timePerFrame: timePerFrame)
}
if you have 10 images named image1, image2, image3 ect..then this is how you would use this function
func runAction(){
let action = setupAnimationWithPrefix("image", start: 1, end: 10, timePerFrame: 0.1)
yourNode.run(action)
} // you can change timePerFrame to your liking i find 0.1 sec per frame the best
Unfortunately you can't override a variable without subclassing it, that is to do something like
class MyTexture: SKTexture {
from there, you could override the variable:
override var filteringMode: SKTextureFilteringMode {
get {
return .nearest
}
set {
}
}
That should work, but now you can't change the filtering mode, ever (without going through a bunch of hooplah).
So the more apt way would be to set the default value via the initializer, but SKTexture has no public designated initializers, so that option is out of the window.
So your best bet is going to be the convenience init extension by Arie, or just make a plain function that returns a new texture:
func nearestTexture(imageNamed name: String) -> SKTexture {
let newTex = SKTexture(imageNamed: name)
newTex.filteringMode = .nearest
return newTex
}
let texture: SKTexture = nearestTexture(imageNamed: "lol")
ActionEditor is still very limited in features, you are going to have to write some kind of code behind to allow for texture filtering. If you do not plan on scaling your sprites (Note I said sprite, not scene, these are two different behaviors), then I would not even worry about texture filtering, the time difference would be negligible.

captureScreen in cocos-2d C++ - how to use or save the captured image?

I want to have a button in my (iOS) app that takes a screenshot of the current screen and then attaches it to a text message.
Like I have seen in this other app...
I have message sending working, and I think I have screenshot working, but I don't know where the screenshot is saved or how to use it.
My message sending is called from a button in the app...
void GameOverScene::messageCallBack(cocos2d::Ref *sender) {
CocosDenshion::SimpleAudioEngine::getInstance()->playEffect(ALL_BUTTONS_CLICK_SOUND_NAME);
utils::captureScreen( CC_CALLBACK_2(GameOverScene::afterCaptured, this), "screenshot.png" );
__String *messageTextOne = __String::create("sms:&body=Hey,%20I%20just%20hit%20a%20score%20of%20");
__String *messageTextTwo = __String::createWithFormat("%i", score);
__String *messageURL = __String::createWithFormat("%s%s", messageTextOne->getCString(), messageTextTwo->getCString());
Application::getInstance()->openURL(messageURL->getCString());
}
and the screenshot function is...
void GameOverScene::afterCaptured( bool succeed, const std::string &outputFile ) {
if (succeed) {
log("Screen capture succeeded");
Size screenSize = Director::getInstance()->getWinSize();
RenderTexture * tex = RenderTexture::create(screenSize.width, screenSize.height);
tex->setPosition(screenSize.width/2, screenSize.height/2);
tex->begin();
this->getParent()->visit();
tex->end();
tex->saveToFile("Image_Save.png", Image::Format::PNG);
} else {
log("Screen capture failed");
}
}
I get the message in the console "Screen capture succeeded", and my message app opens up with the prefilled text message.
What I need to do is to add the screenshot to this message, but I cant see how to do that, or where the screenshot is saved, or how to use the saved screenshot.
If it succeeded it's saved in private application directory. Use software like iExplorer, find your app and it should be inside Documents directory. If you want to see it in Photos you have manually add it there.
In order to achieve this, you have to call
UIImageWriteToSavedPhotosAlbum
see: How to save picture to iPhone photo library?
You have to create a function in AppController.m and use it from here. You can call objc code from c++. It requires UIImage so first you have to pass filepath and load uiimage from it and then finally add to gallery.
You can use something like this to get the path of image saved:
void CustomLayerColor::saveFile(Node*node, std::string fileName, const renderTextureCallback& callBack) {
float renderTextureWidth = node->getBoundingBox().size.width;
float renderTextureHeight = node->getBoundingBox().size.height;
RenderTexture * renderTexture = RenderTexture::create(renderTextureWidth,renderTextureHeight);
renderTexture->begin();
node->visit();
renderTexture->end();
renderTexture->saveToFile(fileName, Image::Format::PNG, true,callBack);
}
void CustomLayerColor::onSaveFileCallBack(RenderTexture * mRenderTexture, const std::string& savedFileNameWithFullPath) {
}

how to access objectAtIndex:indexpath.row xcode?

- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:
(NSInteger)component {
if(thePickerView == self.countryPicker){
self.countryLbl.text = [self.responseArray objectAtIndex:row];
}
}
- (IBAction)countryDoneBtn:(id)sender {
// Picker first row selected on done button
[self.countryLbl setText:[NSString stringWithFormat:#"%#", [self.responseArray
objectAtIndex:[self.countryPicker selectedRowInComponent:0]]]];
[self myViewDown:self.countryView];
[self.durationView removeFromSuperview];
//[post selectState:[self.responseArray objectAtIndex:row ]];
}
i want to do this "[post selectState:[self.responseArray objectAtIndex:row]];"
but can't access "objectAtIndex:row" here in this "- (IBAction)countryDoneBtn:(id)sender"
method.
Please friend help me how can i access the row from this IBAction
Thanks in advance need your help i'm new in iphone development
The row variable is not available because the row you are looking for is only passed in the delegate method didSelectRow:inComponent of the picker view.
Based on you are trying to do, you can still get the selected row of the picker with the method selectedRowInComponent, which you have already used to set the text of the countryLbl.
So, just use the same method to get the object from your responseArray:
NSInteger selectedRow = [self.countryPicker selectedRowInComponent:0];
[post selectState:[self.responseArray objectAtIndex:selectedRow]];