Combining objc with standard Codea commands to make a resizable web browser (code)

 -- ObjcBrowser
function setup()
    viewer.mode = OVERLAY
    -- arbitrarily set width and height of web browser to 400 x 400 pixels
    browserWidth = 400
    browserHeight = 400
    -- set up the iOS view controller to control obj c objects 
    vc = objc.viewer
    -- setup Web browser
    webView = objc.cls.WKWebView()
    -- Autoresizing was older method of configuring screen (turn it off below so can use layout constraints instead)
    webView.translatesAutoresizingMaskIntoConstraints = false  
    -- for any layout constraints that you plan to change dynamically at runtime, best to store the constraint
    -- in a variable (below), make var active property "true" then dynamically change the var constraint property later
    widthLayout = webView.widthAnchor:constraintEqualToConstant_(browserWidth)
    heightLayout = webView.heightAnchor:constraintEqualToConstant_(browserHeight)
    widthLayout.active = true
    heightLayout.active = true
    -- add objc WKWebView to iOS View Controller
    vc.view:addSubview_(webView)
    -- setup Address bar-- use an objc UITextView object
    uiAddressBar = objc.cls.UITextView()
    defaultAddress = "http://www.google.com"
    -- arbitrarily set address bar height to 30
    addressBarHeight = 30
    uiAddressBar.translatesAutoresizingMaskIntoConstraints = false
    -- similar to the webView browser width and height setup above-- store Address bar width in var to later change dynamically
    -- arbitrarily leave 50 pixels space to the right of the address bar to place address go button there later
    widthAddress = uiAddressBar.widthAnchor:constraintEqualToConstant_(browserWidth-50)
    widthAddress.active = true
    vc.view:addSubview_(uiAddressBar)
    -- NSURL is the objc class that handles URLs
    URL = objc.cls.NSURL
    url = URL:URLWithString_(defaultAddress)
    -- now that all the variables are set up draw the objc Browser, load default web page then draw objc Address Bar
    drawBrowser()
    loadWebPage()
    drawAddressBar()
    -- show 4 buttons in the Codea viewer pane that users can use to change the browser's dimensions
    parameter.action("Increase Height", changeSizeWebBrowser)
    parameter.action("Decrease Height", changeSizeWebBrowser)
    parameter.action("Increase Width", changeSizeWebBrowser)
    parameter.action("Decrease Width", changeSizeWebBrowser)
end

function drawBrowser()
    -- in this program, first two anchors (left and top edges of browser) don't change--right (width) and bottom (height) do
    -- Place left end of browser at x coordinate 350 near the middle of screen so doesn't interfere with viewer
    webView.leadingAnchor:constraintEqualToAnchor_constant_(vc.view.leadingAnchor, 350).active = true
    -- place brower 60 pixels below top of the display to leave room for address bar
    --- **Important Note: iOS 2D x, y is top left of display, Codea 2D x,y is bottom left of display**
    webView.topAnchor:constraintEqualToAnchor_constant_(vc.view.topAnchor, 60).active = true
    widthLayout.constant = browserWidth
    heightLayout.constant = browserHeight
end

function drawAddressBar()
    -- uses iOS's UITextView object to make the Address Bar and placed directly above the webView browser
    uiAddressBar.leadingAnchor:constraintEqualToAnchor_constant_(vc.view.leadingAnchor, 350).active = true
    uiAddressBar.topAnchor:constraintEqualToAnchor_constant_(vc.view.topAnchor, 30).active = true
    -- Address Bar is 50 pixels shorter than browser to leave room for the Address Bar go button
    widthAddress.constant = browserWidth-50
    uiAddressBar.heightAnchor:constraintEqualToConstant_(addressBarHeight).active = true
    -- URL is iOS URL class that whose properties contain a lot of information; .host is base of URL address
    -- .text is a property of iOS UITextView
    uiAddressBar.text = "http://"..webView.URL.host
    -- one advantage of using native iOS SDK with Codea objc bridge is that can create nice GUI effects like below
    uiAddressBar.layer.borderWidth = 1
    uiAddressBar.layer.cornerRadius = 5
end

function drawAddressGoButton()
    -- calculate where to put right arrow "go" button next to address bar based on address bar layout & coordinates
    -- in order to get iOS AutoLayout GUI objects to align with regular Codea objects, use .frame property of the
    -- iOS AutoLayout GUI object you are interested in to retrieve .origin.x & .origin.y of iOS GUI object
    -- **Remember that iOS GUI object x,y is top left and Codea x,y coordinates start at bottom left so covert between them**
    -- code below gets iOS GUI objects' x,y coordinates and converts them to Codea x,y coordinates so can add Codea objects
    -- adjacent to them 
    leftAddressBar = uiAddressBar.frame.origin.x
    topAddressBar = uiAddressBar.frame.origin.y
    rightAddressBar = leftAddressBar + uiAddressBar.frame.size.width
    bottomAddressBar = HEIGHT - (topAddressBar + uiAddressBar.frame.size.height)
    goButtonHeight = uiAddressBar.frame.size.height
    goButtonWidth = (webView.frame.origin.x + webView.frame.size.width) - rightAddressBar
    goButtonMiddleX = rightAddressBar + (goButtonWidth/2)
    goButtonMiddleY = bottomAddressBar + (goButtonHeight/2)
    tint(200)
    -- Playbutton is a free sprite picture downloaded from the web of a > arrow in a circle
    sprite(asset.documents.Playbutton,goButtonMiddleX,goButtonMiddleY,goButtonWidth,goButtonHeight)
    
end

function drawNavigationBar()
    -- calculate where to put navigation rectangle bar under browser then put arrows sprite in that rectangle
    -- make rectangle partially transparent so can show black arrow sprites on top of it
    fill(255,200)
    -- This sets the line thickness
    strokeWidth(5)
    -- arbitrarily draw navigation bar rectangle height at 50; again need to convert between iOS WKWebView which draws
    -- starting at top left to Codea coordinates which draws from bottom left
    leftWebView = webView.frame.origin.x
    topWebView = webView.frame.origin.y
    rightWebView = leftWebView + webView.frame.size.width
    bottomWebView = HEIGHT - (topWebView + webView.frame.size.height)
    rectHeight = 50
    rect(leftWebView, (bottomWebView - rectHeight), (rightWebView-leftWebView), rectHeight)
    -- left/right arrow sprite from: <a href="https://www.flaticon.com/free-icons/left-and-right" title="left and right icons">Left and right icons created by Freepik - Flaticon</a>
    -- calculate coordinates below browser to place sprite bidirectional arrows on (similar to calculations for navigation bar
    -- but sprites are placed are centered on their coordinates versus drawn from bottom left)
    -- arbitrarily center sprite arrows below WKWebview browser and size sprite at 50 x 50 pixels
    spriteMiddleX = (leftWebView + rightWebView)/2
    spriteMiddleY = (bottomWebView - (rectHeight/2))
    spriteHeight = 50
    spriteWidth = 50
    sprite(asset.documents["left-and-right-small-triangular-arrows-couple.png"],spriteMiddleX, spriteMiddleY, spriteWidth, spriteHeight) 
end

function loadWebPage()
    -- Turn URL into a "request" so can then be loaded by the webView web browser
    request = objc.cls.NSURLRequest:requestWithURL_(url)
    webView:loadRequest_(request)
end

function touched(touch)
    -- detect touch on left vs right half of bidirectional arrows sprite to determine if user wishes to go browse forward
    -- or backward; in addition, detects touch on the top right address bar "go" arrow to go to address user entered
    -- note the "_" stands for a dummy variable; "parameterless" objc functions use that syntax in Codea
    -- again advantage of using native iOS SDK from objc bridge in Codea-- WKWebView has simple .goBack & .goForward
    -- methods to browse the web
    -- final elseif in this block loads a new web page based on the URL that the user typed in the address bar if the
    -- user touches the "go" arrow
    if touch.state == BEGAN then
        if (touch.pos.x > leftWebView and touch.pos.x < spriteMiddleX and touch.pos.y < bottomWebView and touch.pos.y > (bottomWebView - rectHeight)) then
            _ = webView.goBack
            uiAddressBar.text = "http://"..webView.URL.host
        elseif (touch.pos.x > leftWebView and touch.pos.x < rightWebView and touch.pos.y < bottomWebView and touch.pos.y > (bottomWebView - rectHeight)) then
            _ = webView.goForward
            uiAddressBar.text = "http://"..webView.URL.host
        elseif (touch.pos.x > rightAddressBar and touch.pos.x < (rightAddressBar + goButtonWidth) and touch.pos.y < (HEIGHT-topAddressBar) and touch.pos.y > bottomAddressBar) then
            url = URL:URLWithString_(uiAddressBar.text)
            request = objc.cls.NSURLRequest:requestWithURL_(url)
            webView:loadRequest_(request)
            uiAddressBar.text = "http://"..webView.URL.host
        end
    end
end

function changeSizeWebBrowser(name)
    -- this function dynamically resizes the web browser height & width by 10 pixels based on parameters pressed in viewer
    -- then calls appropriate functions to resize and redraw the browser
    if name == "Increase Height" then browserHeight = browserHeight + 10
    elseif name == "Decrease Height" then browserHeight = browserHeight - 10
    elseif name == "Increase Width" then browserWidth = browserWidth +10
    elseif name == "Decrease Width" then browserWidth = browserWidth - 10
    end
    url = webView.URL
    drawBrowser()
    loadWebPage()
    drawAddressBar()
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)
    -- Do your drawing here:
    -- these calls are to the functions that use standard Codea drawing functions
    drawNavigationBar() 
    drawAddressGoButton()
end

@SugarRay - could you remark this code out with the ~~~ front and back. If you are on a phone this is difficult but, if you’ve typed the code into the editor, exporting the project as a zip and posting the zip file is the best way. Otherwise, @dave1707 may annotate you code for you.

Added ~~~ to the above code.

@SugarRay @Simeon @dave1707 - pasted into new project, changed the graphic assets and an error thrown up, switched to Safari to post a thanks and info then Codea crashed.

Reloaded, finished editing assets anran, immediately threw up another error (image
attached).

p.s. always hoped someone would come up with something like this, so thanks for the project - hope you can resolve the issue.

Sorry it didn’t work for you, @Bri_G. I’ll re-test it the problems you were having by next week. It does crash a lot on me(I think related to the objc bridge not being completely stable yet) but haven’t gotten any errors and when it doesn’t crash it runs fine.

I’ll also know in the future to make a zip from the editor and post the zip.

Have a good week.

Cc: @dave1707 @Simeon

Excellent objc example, just right for me to learn, thank you?

Btw. I had the same error.

@Bri_G and @binaryblues, sorry for the delay in getting back to fixing this.

I’m wondering if something got corrupted with the multiple transfers of pasted code from my original project. The project still seems to work well on my iPad (iPad Pro M1, iPad OS 15.3.1, current App Store release of Codea— at least well in the terms I mentioned as not getting the error that both of you go; it still randomly crashes without showing an error that I think may be related to the state of the development of the obj c bridge in Codea). I figured out how to zip the project from Codea and attached the zipped project below.

When you both get the chance could you try importing and unzipping my zipped project below, and let me know if you still get the same error? I’m still using the same sprites for the play button and left/right arrows that I uploaded earlier in this post.

Thanks!

@SugarRay - Yup, same error and crashed on me. Ran again same error but didn’t Crash. Coouldn’t see the left and right buttons though.

Interesting, even with the error up the size change buttons were still working on the web page.

cc: @Simeon, @dave1707

Note: a little while later, not actually using Codea but on in the background, crashed Codea again.

Thanks for checking, @Bri_G. Are you trying it on a similar configuration to mine (e.g. iPad OS 15.3.1, current App Store release of Codea)? That specific error refers to the part of the code that tries set the iOS URL object (NSURL) to a string that you set in the code (e.g. https://www.google.com). That error though should not effect the resizing of browser since the resizing of the web browser affects a different iOS object (WKWebView) so it might still resize but not load a webpage at a given URL. Interesting, the screenshot you posted previously looks like the app did manage to get to a google server (since it gave you a message from google regarding consent? I’ve never seen that message before). To help me keep troubleshooting:

  1. When the app runs does it even show the main google search page in the browser? If you didn’t change the starting URL, it should load up that google search page. If it does show that start page but then you have trouble going to another URL, then I’ll look at the other part of my code that handles that.

  2. Double checking— the project still showed the error after downloading and unzipping the zip file that I recently posted before changing anything else in the code (except for different sprite buttons)? If so, would make me suspect we are running different configurations of Codea or iOS on our iPads that might be causing the error.

If the only things that differs between the project running on your iPad and my iPad are that we are using different sprites for buttons, I wouldn’t think that difference should have anything to do with an the objective C URL String error. However, I did write the code to set up and place the button sprites using standard Codea commands based on the properties of the web browser size itself defined in objective C. So, if the web browser is not getting sized correctly in objective C it could throw off how the buttons attached to them are getting sized or displayed.

Will also be interested in Simon and Dave’s input. Happy to keep troubleshooting things until works for everyone as hope that everyone will get to take advantage of the objective c bridge in the future.

Have a good night and weekend.

PS, @Bri_G, least crashes if no apps are running in the background of Codea.

Sorry, doesn’t look like I can post a video on this forum to show you how it is supposed to be working.

@SugarRay - I’m using latest iOS on iPad pro 9.7 an latest beta Codea. Will run through again see if I can pick up on your points

If I was you I’d consider setting up a YouTube account to link videos to forums. But that’s a personal preference, videos do come in handy sometimes. Or, you could set up your own website

@SugarRay Thanks for your new version, on my ipad it is ok now.

This is the old crash version:
https://youtu.be/9Bfwtr187vo

This is the new normal version:
https://youtu.be/cKofD94hC2w

Thanks, @Bri_G and @binaryblues. I see now what you meant about setting up a YouTube account and then linking the video to forum. I’ll look into setting such an account up for the future.

Glad to hear that it is working now for binaryblues. Would still like to figure out why not yet working for Bri_G so that others can use it successfully if they try it. Binaryblues are you also on the Codea beta? I’m not so could be one reason why working on mine and note Bri_Gs. Also on newer iPad Pro with M1 processor but not sure if that would have anything to do it.