ObjectiveC integration into Codea — revised demo

Hi, Codea Community.

With the newest Codea release in the app store, the ObjC functionality seems improved and wanted to share a new demo with examples of ObjC integration with standard Codea functionality.

Thanks to @jfperusse for his continued work on the ObjC bridge and helping me figure out how to integrate it in these examples.

Thanks to @RonJeffries for his sample spinning cube code I used in the demo.

Good luck and have fun! :slight_smile:


--# Main
-- ObjcTest2
-- *Note: need to include "Cameras" dependencies to this project from Codea Craft menu in order to touch rotate 3D object

function setup()
    viewer.mode = FULLSCREEN
    -- Create a new 3D Codea Craft scene
    scene = craft.scene()
    -- change the main camera position by "camera" properaty of scene
    scene.camera.z = -4
    -- to actually change the camera's function (e.g. it's viewport) need to actually get the camera element of camera element
    scene.cameraElement = scene.camera:get(craft.camera)
    -- to change the scene camera's viewport (i.e. where 3D scene drawn on display) pass relative values x,y,width,height
    -- note: don't need to include the WIDTH/WIDTH or HEIGHT/HEIGHT expressions but written to illustrate using relative values
    scene.cameraElement:viewport((0.65*(WIDTH/WIDTH)),(0.15*(HEIGHT/HEIGHT)),(0.33*(WIDTH/WIDTH)),(0.5*(HEIGHT/HEIGHT)))
    
    -- Create a new 3D Codea entity to display in a 3D viewport portion of the screen
    Cube = scene:entity()
    Cube.model = craft.model.cube(vec3(1,1,1))
    Cube.material = craft.material(asset.builtin.Materials.Basic)
    Cube.material.map = readImage(asset.builtin.Blocks.Missing)
    Cube.eulerAngles = vec3(0,0,0) 
    scene.camera.z = -4
    angle = 0
    
    -- add OrbitViewer (come with Craft "Cameras" dependency attached)
    scene.camera:add(OrbitViewer, Cube.position, 10, 5, 20)
    
    --**Final part of setup below sets up ObjC code to work with Codea**
    
    --note: the next statement gets the actual UIViewController running in Codea
    local vc = objc.viewer
    
    --note: the next statement gets makes an instance (new) UI text view that you can use to make a GUI textbox
    uiTextView = objc.cls.UITextView()
    vc.view:addSubview_(uiTextView)
    
    --note: need to make the next ObjC statement false to allow use of advanced Apple ObjC display formatting constraints
    uiTextView.translatesAutoresizingMaskIntoConstraints = false
    
    -- Pin the trailing edge of the ObjC textbox (uiTextView) 20 in from right side of screen
    uiTextView.trailingAnchor:constraintEqualToAnchor_constant_(vc.view.trailingAnchor, -20).active = true
    
    -- Pin the top edge of the ObjC textbox (uiTextView) 20 down from top of screen)
    uiTextView.topAnchor:constraintEqualToAnchor_constant_(vc.view.topAnchor, 20).active = true
    
    -- Set the width and height of ObjC textbox (uiTextView)
    uiTextView.widthAnchor:constraintEqualToConstant_(400).active = true
    uiTextView.heightAnchor:constraintEqualToConstant_(200).active = true
    uiTextView.text = "Hello World."
    -- Apple's ObjC GUI objects like uiTextView allow lots of customization
    uiTextView.layer.cornerRadius = 8
    
    -- placeholder variable for what user types in the textbox
    typedText=""
    
    --create a delegate to work with the UITextView that will sense when changes are made to the textbox
    Delegate = objc.delegate("UITextViewDelegate")
    
    function Delegate:textViewShouldBeginEditing_(objTextView)
        -- replace with false to prevent editing textbox
        return true
    end
    
    function Delegate:textViewDidChange_(objTextView)
        typedText = objTextView.text
    end
    
    -- set delegate to textView AFTER you've created the delegate above
    uiTextView.delegate = Delegate()
    
    -- SFSafariViewController is an ObjC fully-functional self-contained browser that you can resize & include in Codea app
    local URL = objc.cls.NSURL
    local url = URL:URLWithString_("http://www.google.com")
    local SFSafariViewController = objc.cls.SFSafariViewController
    local safariBrowser = SFSafariViewController:alloc():initWithURL_(url)
    vc:addChildViewController_(safariBrowser)
    vc.view:addSubview_(safariBrowser.view)
    safariBrowser.view.translatesAutoresizingMaskIntoConstraints = false
    safariBrowser.view.leadingAnchor:constraintEqualToAnchor_constant_(vc.view.leadingAnchor, 20).active = true
    safariBrowser.view.topAnchor:constraintEqualToAnchor_constant_(vc.view.topAnchor, 20).active = true
    safariBrowser.view.widthAnchor:constraintEqualToConstant_(450).active = true
    safariBrowser.view.heightAnchor:constraintEqualToConstant_(700).active = true   
end

function update(dt)
    -- Update the Codea 3D Craft scene (physics, transforms etc)
    angle = angle + 1
    Cube.eulerAngles = vec3(angle/10, angle, angle/5)
    scene:update(dt)
end

function draw()
    background(0)
    update(DeltaTime)
    
    -- Actually draw a 3d Codea Craft scene
    scene:draw()
    
    -- Use standard Codea Graphics to write to the screen what is in the ObjC Textbox
    fill(160, 223, 157)
    textWrapWidth(260)
    text(typedText, WIDTH/2 + 20, HEIGHT-200)
end

Very cool. One thing I didn’t understand, the (WIDTH/WIDTH) etc in this:

scene.cameraelement:viewport((0.65*(WIDTH/WIDTH)),(0.15*(HEIGHT/HEIGHT)),(0.33*(WIDTH/WIDTH)),(0.5*(HEIGHT/HEIGHT)))

What’s up with that? Just documentation?

Yes, sorry, to be confusing, @RonJeffries. I probably should have left that out that syntax (a previous forum member wasn’t fond of it either on a prior post). I wanted to emphasize that the coordinates are relative coordinates and not actual x,y coordinates which confused me and wrote it that way to emphasize it to others who want to change the viewport’s size. I’ll change it for any future posts.

@SugarRay - loaded up and ran your demo, worked fine until I started playing with the Google window. Switched windows to Safari and Codea crashed. Ran again and didn’t crash, was able to access other sites. But it did crash when I switched windows to parallel Safari.

P.s. submitted crash report via iPad system crash report.

Copy to: @dave1707 @Simeon @jfperusse

I did see one crash as well, also submitted.

Thanks, and sorry, @Bri_G and @RonJeffries—when I tested out going to different sites it worked for me. When you stated that it crashed when you played with the Google window, did you mean when you went navigated to different websites or when you changed aspects of the web browser in the code and tried to run it?

@SugarRay - the first time I ran it I just scrolled the webpage window. I then switched to Safari in parellel and it was then that I got the Codea crash report. Maybe because it lost the focus?

I thouhgt it might have been when I left Codea, as new versions of Codea always seem to crash the first time they are run. So I was thinking something in the linkage may have been lost in a similar manner.

The second time it all seemed to be running but when I switched to parallel Safari I got the crash report again.

Perhaps you can get feedback from @Simeon on what errors were involved.

Okay, thanks for clarifying, @Bri_G.

@Simeon — is there a way we can save crash logs in Codea?

I’m not sure what I was doing when mine crashed. I’ll try to pay more attention next time.

Thanks!

@SugarRay I haven’t played with it enough to hit any bugs.

I just typed in some text and then double-tapped it to select it, and when it worked…

IT WAS AMAZING.

This is really, really great.

Wow, that’s great to hear @UberGoober! I think @jfperusse is continuing to work on refinements in the objective C bridge so should continue to get better and better :slight_smile: Have a good weekend.

@SugarRay @jfperusse Thanks for your hard work. The objc is very useful and the UITextView has all sorts of properties, but I have two problems that I don’t know how anyone else has solved:

  • How Do I destroy it? A lot of usage scenarios are to pop up a text box, the user enters the text, determines the input, and then closes the pop up text box, but here, I tried a lot of ways, can make this text box disappear from the screen.
  • How to set a color by using objc.cls.UIColor, I tried several UIColor functions and found none of them worked(just like: objc.cls.uicolor:greenColor(), objc.cls.UIColor:colorWithHex(“333333”))

Hi, @binaryblues. For the first question I have a solution (if you at least are looking just to hide the text box and not actually permanently destroy it):

uiTextView.hidden = true

(It sometimes is confusing to find things online in Apple’s documentation; if you go to the bottom of the UITextView documentation it states that UITextView inherits from UIScrollView and if you go to the bottom of that page’s documentation, it states UIScrollView inherits from UIView and you’ll find the “hidden” property listed no that page. Remember to choose the “ObjectiveC” form of the documentation on the top right of the web page (e.g. in Swift the property is called “isHidden” instead).

For the second question, I believe it is just a current bug in the Codea Objective C bridge; I had written to @jfperusse before about trying to access the iOS Objective C system colors and none of them worked for me or him when he tried it. Will see if @jfperusse has any updates but may need to wait for future Codea Objective C bridge to be able to access the iOS Objective C system colors.

Hope that helps :slight_smile:

@jfperusse I’m wondering if it’s possible to have something like an objc.my syntax for accessing custom classes and variables, where essentially in Codea everything after my is ignored, but once running in Xcode the objc environment would attach the custom classes and variables to those calls.

So in Codea you’d write something like:


    local adjustMe = 4
    adjustMe = objc.my.customMethod(adjustMe) --in Codea this always returns nil
    if adjustMe ~= nil then
        --more code
    end

So essentially in Codea the syntax would replace everything tagged obj.my with nil, but in Xcode the syntax would access the actual methods and variables named (of course it’s up to the user to make sure those methods and variables exist).

Would that be possible?

@SugarRay It works very well! Thanks! Now I can use it in Codea to make a powerful text box control with few lines of code?

You’re welcome, @binaryblues. Glad it worked :slight_smile:

Hi @SugarRay, @UberGoober.

Sorry for the delay. For the colors, I confirm it is yet to be supported.

For the objc.my suggestion, I am not sure I understand it right. If objc.my would result in nil, then wouldn’t the following “.customMethod” cause an error?

I believe you can already check if you are running from an exported project. Would that solve your problem?

@jfperusse I think maybe I explained the idea badly.

So say I have a custom objective-C class called TurnManager that has an int called currentTurn and a function called setTurnNumber.

If I wanted to use it to print the current turn in the middle of the screen I could do something like:


local currentTurn = objc.my.TurnManager.currentTurn or 1
text( “turn: “..currentTurn, WIDTH/2, HEIGHT/2)

Because Codea would read everything after objc.my as nil, this would always print out “turn: 1” when running in Codea, but when running in Xcode it would correctly show the value in TurnManager.currentTurn.

That seems pretty conceptually straightforward for accessing custom variables in the objective-C environment—if they always evaluate to nil then we can provide default values inside Codea but have them use the real values in Xcode.

Functions could work similarly:


_ = objc.my.TurnManager.setTurnNumber(4)

If Codea read everything after objc.my as nil, Codea would see this as assigning nil to an unused variable name, basically ignoring it entirely, but when run in the objective-C environment it would actually call the given function with a parameter of 4.

I don’t know how plausible the objective-C side of things would be in these examples, but (again at least conceptually) on the Codea side turning everything after objc.my into nil would make it simple to write code that was basically ignored inside Codea but that functioned correctly inside Xcode.

@UberGoober One thing I had discussed with @Simeon was adding viewer.isStandalone which would allow you to easily tell if you are running from a compiled exported project. You could then do:

local currentTurn = viewer.isStandalone and objc.TurnManager.currentTurn or 1

If this works for you, you could temporarily use the sample under objc.info documentation until we make it available as viewer.isStandalone.