나의 발자취

iOS WebKit: 웹뷰 frame 크기 조절, 버튼 넣기 본문

앱 개발/iOS

iOS WebKit: 웹뷰 frame 크기 조절, 버튼 넣기

달모드 2024. 9. 5. 15:06

서로 다른 기능을 하는 JS 함수를 Swift에서 받아 처리해 버튼으로 상호작용하는 방법에 대해 알아보겠다.

(버튼이 있을 위치를 위해)

1. 웹뷰 frame 크기 조절

지난번에 이어서 프레임 사이즈를 조절해줄 것이다.

메서드의 파라미터로 frame: view.frame로 넘겨준것에서, frame 변수를 생성하여 별도로 크기를 정의하기로 한다.

 

<before>

var webView = WKWebView(frame: view.frame, configuration: webViewConfig)

 

<after>

let frame = CGRect(origin: view.frame.origin, size: CGSize(width: view.frame.width, height: view.frame.height/3*2))
var webView = WKWebView(frame: frame, configuration: webViewConfig)

 

2. JS 함수와 interact하는 버튼 추가 (1 out of 3)

<JS>

 function call_func1() {
        document.getElementById('title1').innerText = 'call_func1';
      }

일단 버튼을 스토리보드에 생성 후 IBAction을 추가해준다.

 

그다음 .evaluateJavaScript 메서드를 사용하여 자바스크립트 내에 구현한 메서드를 호출해서 가져온다!

<JS>

 

<Swift>

 

그런데 이렇게 하면 webView 변수는 위에 블록에서 정의했으므로 접근할 수 없어서 위와 같이 에러가 나게 된다.

 

따라서 webView 변수를 viewDidLoad() 위로 빼준다.

위로 빼주는데, 형태는 옵셔널이 될것이다.

 

따라서 아래에 구현해놓은 코드들 중에 webView 변수가 들어간 코드는 에러가 나게 된다. (webView 변수가 옵셔널 타입으로 선언이 되었으므로)

 

그래서 해당하는 부분은 옵셔널 체이닝으로 옵셔널 언래핑을 해주는 것...

1. view.addSubview(webView!)

2. webView?.load(request)

3. 버튼의 액션함수 내 webView 객제 접근할때에도 옵셔널 체이닝을 해준다.

⚫️ Button 1 코드

@IBAction func action1(_ sender: Any) {
        webView?.evaluateJavaScript("call_func1()")
    }

 

 

 

 

그 다음으로, 두번째 버튼 구현

3. 매개변수가 있는 JS 함수와 interact하는 버튼 추가 (2 out of 3)

<JS>

function call_func2(title) {
        document.getElementById('title1').innerText = title;
      }

 

<Swift>

만약 JS에서 매개변수를 받는 함수의 경우는 아래와 같이 JS 매개변수를 '(콜론)으로 받아야한다!!!

⚫️ Button 2 코드

  @IBAction func action2(_ sender: Any) {
        let name = "aespa"
        webView?.evaluateJavaScript("call_func2('\(name)')")
    }

 

 

그럼 아래와 같이 가운데 버튼을 클릭했을 때 위의 title이 'aespa'로 제대로 바뀌는 것을 볼 수 있다.

 

 

4. JSON을 데이터로 받아 파싱 처리를 하는 JS 함수와 interact하는 버튼 추가 (3 out of 3)

<JS>

function call_func3(data) {
        var obj = JSON.parse(data);
        document.getElementById('title1').innerText = obj['name'];
        obj.name = '우영우';
        return obj;

 

 

 

1) JSON을 Data 타입으로 받아 JSONSerialization

JSON 데이터를 받으려면 모든 플랫폼에 종속되지 않고 데이터를 받아야하므로, 'Data' 타입으로 받을 것이다.

let infoData: Data = JSONSerialization.data(withJSONObject: info)

 

  • swift에서, 파라미터 데이터를 직렬화시키는 것을 serializedData 라고 한다.
  • JSONSerialization 클래스를 이용해서, JSON 데이터를 파싱할 수 있다.
  • throws로 반환하는 형태로 되어있으면, try-catch로 예외처리를 해주어야한다.
  • throws 에 대한 설명: Data를 가져올 때, 예외는 발생할 수 있으나, 데이터를 내 마음대로 예외처리하면 안되기 때문에, 이것을 호출하는 쪽으로 던져주는 처리를 하는 방법이다.
let info = ["phone": "010-1111-1111", "email": "a@a.com",
        "name": "홍길동", "gender": "male"]
        do {
            let infoData: Data = try JSONSerialization.data(withJSONObject: info)
        } catch {
            print("error occured")
        }

 

 

2) 인코딩

let encodedData = String(data: infoData, encoding: .utf8)

  • 한글 인코딩은 utf8을 쓴다.

 

다음,

 

webView?.evaluateJavaScript("call_func3('\(encodedData')", completionHandler: <#T##((Any?, (any Error)?) -> Void)?##((Any?, (any Error)?) -> Void)?##(Any?, (any Error)?) -> Void#>)

 

을 해주면서, encodedData 변수에 대해 guard-let 으로 옵셔널을 해제해주었다.

 

⚫️ Button 3 코드

@IBAction func action3(_ sender: Any) {
        let info = ["phone": "010-1111-1111", "email": "a@a.com",
        "name": "홍길동", "gender": "male"]
        do {
            let infoData: Data = try JSONSerialization.data(withJSONObject: info)
            let encodedData = String(data: infoData, encoding: .utf8)
        } catch {
            print("error occured")
        }
        
    }

 

 

최종 코드


import UIKit
import WebKit

class ViewController: UIViewController {

    var webView: WKWebView?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
        let userContent = WKUserContentController()
        userContent.add(self, name: "SendMessage")
        userContent.add(self, name: "SayHello")
        userContent.add(self, name: "SetUser")
 
        let webViewConfig = WKWebViewConfiguration()
        webViewConfig.userContentController = userContent
        
        let frame = CGRect(origin: view.frame.origin, size: CGSize(width: view.frame.width, height: view.frame.height/3*2))
        
        webView = WKWebView(frame: frame, configuration: webViewConfig)
        
        view.addSubview(webView!)
        
        // request 생성
        guard let url = URL(string: "http://127.0.0.1:8080") else { return }
        let request = URLRequest(url: url)
        webView?.load(request)
        
    }

    // Add Buttons

    @IBAction func action1(_ sender: Any) {
        webView?.evaluateJavaScript("call_func1()")
    }
    @IBAction func action2(_ sender: Any) {
        let name = "aespa"
        webView?.evaluateJavaScript("call_func2('\(name)')")
    }
    @IBAction func action3(_ sender: Any) {
        let info = ["phone": "010-1111-1111", "email": "a@a.com","name": "홍길동", "gender": "male"]
        
        do {
            // data 정렬화
            let infoData: Data = try JSONSerialization.data(withJSONObject: info)
            // data 인코딩
            guard let encodedData = String(data: infoData, encoding: .utf8) else { return }
            // JS function과 통신
            webView?.evaluateJavaScript("call_func3('\(encodedData)')",
            completionHandler: {result, error in
            if let error {return}
            guard let info = result as? [String:String] else {return}
            print(info)
            })
            } catch {
                print("error occured")
        }
        
    }
    
    
}
extension ViewController: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        switch message.name {
        case "SendMessage":
            print("SendMessage")
            print(message.body)
            
        case "SayHello":
            print("SayHello")
            print(message.body)
            
        case "SetUser":
            print("SetUser")
            print(message.body)
            if let info = message.body as? [String:String] {
                print(info["name"]!, info["email"]!, info["gender"]!)
            }
        default:
            print("정의되지 않은 메시지")
        }
        
    }
}
728x90
반응형
Comments