Tim
Tim

Reputation: 667

Qt6 IOS QML QIOSApplicationDelegate starts delegate but Main.qml not visible (screen is black)

Ive spent many hours trying to get a simple QML IOS application to start the UIApplication delegate as well as start and display the Main.qml component for a larger app I would like to create.

Im using Qt 6.8.1 on MacOS Sonoma 14.6.1, Qt Creator 15.0.0 Ive tried running it on both the IOS Simulator as well as an Iphone Device w/same result.

The problem is that everything seems to work except that the Main.qml is not showing. The Screen is just black. I can close the app and open it and the UIApplication delegates are getting called as expected but the screen is always black. Ive tried multiple other solutions none of which I have been able to get thing to work correctly.

Ive included the output when I run it so you can see things appear to be working as expected except for the visual display.

Any help and/or suggestions are appreciated. Thank you!

Main.qml:

import QtQuick
import QtQuick.Window
import QtQuick.Controls

ApplicationWindow {
    width: 640
    height: 480
    visible: true
    color: "green"

    Component.onCompleted: {
        console.log("Main.qml Completed")
    }

    onVisibleChanged: console.log("Main.qml: visible: " + visible)

    Text {
        text: "My Application"
        color: "white"
        anchors.centerIn: parent
        font.pixelSize: 26
    }
}

AppDelegate.h:

#import <UIKit/UIKit.h>

@interface QIOSApplicationDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

AppDelegate.mm

// AppDelegate.mm
#import "AppDelegate.h"

extern void setContinueLaunchingQML();

@implementation QIOSApplicationDelegate

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    NSLog(@"------------ App applicationDidFinishLaunching");
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    NSLog(@"==== didFinishLaunchingWithOptions");

    setContinueLaunchingQML();

    NSLog(@"==== applicationDidFinishLaunching return YES ====");
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
    NSLog(@"IOS_APP_DEL: applicationWillResignActive");
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSLog(@"IOS_APP_DEL: applicationDidEnterBackground");
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
    NSLog(@"IOS_APP_DEL: applicationWillEnterForeground");
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
    NSLog(@"IOS_APP_DEL: applicationDidBecomeActive");
}
- (void)applicationWillTerminate:(UIApplication *)application {
}
@end

main.cpp:

// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include <unistd.h>

static bool waiting_for_ios_delegate_ = true;

int main(int argc, char *argv[])
{
    qDebug() << "MAIN CPP start";

    while(waiting_for_ios_delegate_) {
        sleep(1);
        qApp->processEvents();
    }

    qDebug() << "Continue main cpp Startup...";

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;

    QQmlComponent component(&engine);
    component.loadFromModule("iosDelegate", "Main");
    if (component.isReady()) {
        qDebug() << "Component Main.qml READY...now create it";
        if (!component.create()) {
            qWarning() << "ERROR - Main.qml failed to create";
            _exit(1);
        }
    }
    else {
        qWarning() << "ERROR - Main.qml is not ready - " << component.errorString();
        _exit(1);
    }

    qDebug() << "exec()";
    return app.exec();
}

void setContinueLaunchingQML()
{
    qDebug() << "setContinueLaunchingQML";
    waiting_for_ios_delegate_ = false;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)

project(iosDelegate VERSION 0.1 LANGUAGES CXX OBJCXX)

find_package(Qt6 REQUIRED COMPONENTS
    Core
    Qml
    Gui
    Quick
    QuickControls2
    Widgets)

qt_standard_project_setup(REQUIRES 6.5)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -x objective-c++ -fobjc-arc
    -I./*.h
    -iwithprefix /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/
    -iwithprefix /Library/Developer/CommandLineTools/SDKs/MacOSX15.1.sdk/System/Library/Frameworks/
    ")

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}
    -framework Foundation
    -framework UIKit
")

if(APPLE)
    set(APPLE_SDK_TOP /Applications/Xcode.app/Contents/Developer/Platforms)
    set(IOS_SYS_LIB_FRAMEWORK System/Library/Frameworks)

    if(CMAKE_OSX_SYSROOT STREQUAL "iphonesimulator")
        set(IOS_PLATFORM iPhoneSimulator)
    elseif (CMAKE_OSX_SYSROOT STREQUAL "iphoneos")
        set(IOS_PLATFORM iPhoneOS)
    else()
        warning(IOS_PLATFORM NOT FOUND)
    endif()
endif(APPLE)

qt_add_executable(appiosDelegate
    main.cpp
    AppDelegate.h
    AppDelegate.mm
)

qt_add_qml_module(appiosDelegate
    URI iosDelegate
    VERSION 1.0
    QML_FILES
        Main.qml
)

set_target_properties(appiosDelegate PROPERTIES
    MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
    MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
    MACOSX_BUNDLE TRUE
    WIN32_EXECUTABLE TRUE
)

target_link_libraries(appiosDelegate
    PRIVATE
    Qt6::Core
    Qt6::Qml
    Qt6::Quick
    Qt6::Gui
    Qt6::QuickControls2
    Qt6::Widgets
)

if(QT_VERSION_MAJOR EQUAL 6)
    qt_finalize_executable(${TARGET})
endif()

set(CMAKE_OSX_INFOPLIST_FILE "${CMAKE_CURRENT_LIST_DIR}/Info.plist")
set(MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_LIST_DIR}/Info.plist")

include(GNUInstallDirs)
install(TARGETS appiosDelegate
    BUNDLE DESTINATION .
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

Output:

2025-01-17 11:05:07.432 appiosDelegate[50772:16574720] ==== didFinishLaunchingWithOptions
setContinueLaunchingQML
2025-01-17 11:05:07.434 appiosDelegate[50772:16574720] ==== applicationDidFinishLaunching return YES ====
MAIN CPP start
Continue main cpp Startup...
Component Main.qml READY...now create it
qml: Main.qml: visible: true
qml: Main.qml Completed
exec()
2025-01-17 11:05:08.229 appiosDelegate[50772:16574720] IOS_APP_DEL: applicationDidBecomeActive
2025-01-17 11:12:39.336 appiosDelegate[50772:16574720] IOS_APP_DEL: applicationWillResignActive
2025-01-17 11:12:39.972 appiosDelegate[50772:16574720] IOS_APP_DEL: applicationDidEnterBackground
2025-01-17 11:12:44.486 appiosDelegate[50772:16574720] IOS_APP_DEL: applicationWillEnterForeground
2025-01-17 11:12:44.764 appiosDelegate[50772:16574720] IOS_APP_DEL: applicationDidBecomeActive

[EDIT]

NOTE: Main.qml is now visible but now the other IOS UIApplicationDelegates are not being called (other than didFinishLaunchingWithOptions after doing the following:

The only 2 changes I made are as follows:

// AppDelegate.h
#import <UIKit/UIKit.h>

@interface QIOSApplicationDelegate
@end

@interface QIOSApplicationDelegate (MyIOSApplicationDelegate)
@property (strong, nonatomic) UIWindow *window;
@end

I also removed the wait flag entirely: waiting_for_ios_delegate_

Now the output is:

2025-01-28 05:24:04.223 appiosDelegate[81506:24556235] ==== didFinishLaunchingWithOptions
2025-01-28 05:24:04.223 appiosDelegate[81506:24556235] ==== applicationDidFinishLaunching return YES ====
MAIN CPP start
Component Main.qml READY...now create it
qml: Main.qml: visible: true
qml: Main.qml Completed
exec()
stale focus object 0x0 , doing manual update

In my first examples the other delegates were called when I simulated the "HOME" screen and also restarted the app. After my 2 changes I only get the 1 delegate called, no others are being called.

I also tried other suggestions from articles with the same result as what I just described here using:

extern "C" int qt_main_wrapper(int argc, char **argv[]);

and implementing the IOS entry point _qt_main_wrapper:

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}
    -Wl,-e,_qt_main_wrapper
    -u _qt_registerPlatformPlugin
    -framework Foundation
    -framework UIKit")

However it also seems that if I dont include -Wl,-e,_qt_main_wrapper I get the exact same result in my output:

2025-01-28 07:25:02.170 appiosDelegate[93577:24696424] ==== didFinishLaunchingWithOptions
2025-01-28 07:25:02.170 appiosDelegate[93577:24696424] ==== applicationDidFinishLaunching return YES ====
MAIN CPP start
Continue main cpp Startup...
Component Main.qml READY...now create it
qml: Main.qml: visible: true
qml: Main.qml Completed
exec()
stale focus object 0x0 , doing manual update

Other articles:

Qt XCode iOS entry point

QT IOS linker error entry point (_main) undefined

Allow dynamic builds of Qt for iOS - Tech Preview

How to get didFinishLaunchingWithOptions called in IOS delegate?

I have not been able to find a Qt document describing how to accomplish this other than the bug report link I provided.

I also tried setting:

export QT_LOGGING_RULES="lcEventDispatcher.debug=true"

When I tried -Wl,-e,_qt_main_wrapper link flag to see if qt_main_wrapper was being called I did not see any output for that event (This should come from qioseventdispatcher.mm). I have no idea why that linker flag would not work since I am seeing it in the compiler output and I did add the:

extern "C" int qt_main_wrapper(int argc, char *argv[])

to my main.cpp when I tried it. It seems like Im one step away from solving this however it still eludes me. Any help is appreciated.

Upvotes: 0

Views: 52

Answers (0)

Related Questions