I'm having an issue with WKWebview.
I'm loading a canvas from html into the Webview.
This is the link:
https://cdn-factory.marketjs.com/en/color-fill-playable-ad-demo/index.html
it runs perfectly the html game loads and everything seems to be okay, the touch events are working when you play the mini game tough there is a button "install now"
that should open the app store url and when i tap it nothing happens.
On browser it works and i have tried few of those games but nothings works.
Any advise? the webview preference allowjs and allow inline media are set to true.
Aside that i couldn't find any info on it.
Help is appreciated :D thanks !
iOS: 13.4.1
Swift: 5
iPhone xs max
Okay i figured it out.
In case someone will bash into it in the future.
So the webpage used:
window.open("http://asdfsadfsda")
javascript code to open the url in a new window.
WKWebview wont do anything unless you will implement the WKUIDelegate and the method
webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration,
for navigationAction: WKNavigationAction,
windowFeatures: WKWindowFeatures) -> WKWebView?;
another important thing in order to make this method to get called is to set in the webview preferences like this:
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
configuration.allowsInlineMediaPlayback = true;
configuration.preferences.javaScriptCanOpenWindowsAutomatically = true
configuration.preferences.javaScriptEnabled = true
And then you can handle the new page opening in the delegate method.
If you still want a new window you should create a new webview and return it in the delegate method.
In my case it was to do nothing and just take the url etc..
so i returned nil.
Good luck :)
Related
I try to add a skip-intro-button (like Netflix) to the AVPlayerViewController of a tvOS movie app. I added it as subview to the contentOverlayView and when I let it appear I force giving it the focus with preferredFocusEnvironments. Everything's fine...
Until the user navigates somewhere else (e.g. seek bar or video asset info view). The button then loses focus (expected) and never can be focused again by user interaction.
I tried to:
add a UIFocusGuide()
put my button directly on the AVPlayerViewController's view
add a own subview, which contains the buttons to the AVPlayerViewController's view
add a own subview, which contains the buttons to the contentOverlayView
add several other buttons next to, below, above my button on the same subview (for each of the cases above)
The last approach shows that none of the other buttons can ever get focus by user interaction, so it seems, that, for the same reason, the skip-intro-button cannot be focused by the user. But what is this reason?
What is the right practice to add custom interaction elements to AVPlayerViewController?
Anyone any ideas?
Try to answer my main question "What is the right practice to add custom interaction elements to AVPlayerViewController?" myself:
Since I posted this question, 1.5 years ago, I didn't change the implementation of the skip intro feature very much. So whenever the user uses the remote, the button will lose focus and the only thing I changed is, that I hide the button whenever this happens, by implementing didUpdateFocus(in:with:), similar to this:
if let previouslyFocusedView = context.previouslyFocusedView {
if previouslyFocusedView == skipIntroButton {
changeSkipIntroButtonVisibility(to: 0.0)//animates alpha value
}
}
(I'm not completely sure, why I don't set it to isHidden = true)
However, in the meantime I had to implement a more complex overlay to our player, i.e. a "Start Next Video / Watch Credits" thing, with a teaser, some buttons, a countdown/progress bar and more. With the problems described above, it is obvious, that I couldn't go with the contentOverlayView approach.
So I decided to implement it the "traditional way", by presenting a complete UIViewController on top of the player's view, like this:
func showNextVideoOverlay() {
guard let nextVideoTeaser = nextVideoTeaser else { return }
let nextVideoOverlay = NextVideoAnnouncementViewController(withTeaser: nextVideoTeaser, player: player)
nextVideoOverlay.nextVideoAnnouncementDelegate = self
nextVideoOverlay.modalPresentationStyle = .overFullScreen
present(nextVideoOverlay, animated: true, completion: nil)
}
Of course the NextVideoAnnouncementViewController is transparent, so video watching is still possible. I turned out that this straight forward approach works pretty well and I really don't know, why I haven't thought about it, when implementing skip intro.
My colleagues in QA found one tricky thing, you should be aware of (and I think, I remember, that this is different with different Devices and different remotes and on different tvOS versions - try it):
The overlying view controller blocks most of the commands coming from the remote, respectively you can navigate inside the view controller without affecting the player - except the play/pause button. That one will pause the player, but it's not possible to resume from there. This is, because a playing (av)player always listens to this button, while a paused one doesn't. So I also had to implement something like this:
func addRemoteButtonRecognizer() {
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(playPauseButtonPressed))
tapRecognizer.allowedPressTypes = [NSNumber(value: UIPress.PressType.playPause.rawValue)]
self.view.addGestureRecognizer(tapRecognizer)
}
#objc func playPauseButtonPressed(sender: AnyObject) {
nextVideoAnnouncementDelegate?.remotePlayButtonPressed()
}
func remotePlayButtonPressed() {
if player?.playerStatus == .paused {
player?.play()
} else {
player?.pause()
}
}
I hope this helps some of you, who come by here and find no other answer.
I would like to create several tests for native iOS application. To be more precise, I want to test deep links. But I am not sure how to trigger deep link with XCUITest and I don't really see how launch() and launcArguments (https://developer.apple.com/documentation/xctest/xcuiapplication) can help me. Did anybody have a chance to open deep link with XCUITest?
In iOS 14 using Spotlight seems to work well:
private func open(_ urlString: String) {
XCUIDevice.shared.press(.home)
XCUIApplication(bundleIdentifier: "com.apple.springboard").swipeDown()
let spotlight = XCUIApplication(bundleIdentifier: "com.apple.Spotlight")
spotlight.textFields["SpotlightSearchField"].typeText(urlString)
spotlight.buttons["Go"].tap()
}
Set safari as app like this
let safari = XCUIApplication(bundleIdentifier: "com.apple.mobilesafari")
Open your email in safari
Tap on the link
Normally assert some element on app
I never tried this, but this idea comes to my mind. Create a new dummy project/application that should only contain some links pointing to URLs that you expect your original app to open. From that new application, write some UI tests that tap a link, like this:
func testOpeningLinks() {
let app = XCUIApplication()
app.links["Some link text"].tap()
// This is the place where your original app should be opened...
// Find the XCUIApplication object:
let originalApp = XCUIApplication(bundleIdentifier: "original.app.bundle.identifier")
// You should be able to find some views from original app from here, eg. a button:
let button = originalApp.buttons.element
}
This would work only if you have previously installed your app on the device/simulator where you're running your UI tests.
I am trying to override the sound that plays when a focus change is made on tvOS, but I cannot seem to find anything indicating if this is possible. I have looked through the Apple documentation a bit, and looked at some of the sound API's but none seemed to fit. Does anybody know if this is even possible? If this is possible how can this be achieved?
This can be achieved with soundIdentifierForFocusUpdate, which was added to the SDK in tvOS 11
Using this method, you can customize or remove the default sound of tvOS played on focus updates.
To remove the sound you can return UIFocusSoundIdentifier.none
override func soundIdentifierForFocusUpdate(in context: UIFocusUpdateContext) -> UIFocusSoundIdentifier? {
return UIFocusSoundIdentifier.none
}
To use a different sound insted, you must include the new sound file in your target, and to load as shown here below:
let myPing = UIFocusSoundIdentifier.init(rawValue: "customPing")
let soundURL = Bundle.main.url(forResource: "ping", withExtension: "aif")!
UIFocusSystem.register(_: soundURL, forSoundIdentifier: myPing)
Then you have to return that sound new from soundIdentifierForFocusUpdate:
override func soundIdentifierForFocusUpdate(in context: UIFocusUpdateContext) -> UIFocusSoundIdentifier? {
return myPing
}
Everything is documented by Apple in the following link:
Using Custom Sounds for Focus Movement
I've run into a strange bug with MFMessageComposeViewController in iOS8, that is only reproducible within my own app. It's affecting the app right now in the App Store, built with the iOS7 SDK running on iOS8, as well with the iOS8 SDK in my own test devices (iPhone and iPad)
The issue is that when I bring up the MFMessageComposeViewController, it shows me the controller without a text field or a Send button anymore. I haven't changed my code between iOS7 and iOS8, so not sure why this is happening. The code itself is very simple:
MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init];
picker.messageComposeDelegate = self;
[picker setRecipients: #[#"5551112222"]];
[picker setBody: #"Test"];
[self presentViewController:picker animated:YES completion: ^{
NSLog(#"MFMessageComposeViewController completion handler");
}];
This is what it looks like:
Any ideas for what I can try for a work-around? I've tried setting the textField and recipients in the completion handler; tried calling becomeFirstResponder on the top-most view controller; no luck. Again, this was/is working perfectly fine in iOS7.
EDIT:
So I found Apple's own sample code for MFMessageComposeViewController from this link: https://developer.apple.com/library/ios/samplecode/MessageComposer/Listings/MessageComposerViewController_m.html
When I build and run this app, the MFMessageComposeViewController shows up perfectly, and pre-filling the phone number and text fields works as well. But when I copy their files into my app, make their storyboard my main storyboard, press the "Compose SMS" button and I see the same exact problem!
What is going on here? Very confused. Could it be some configuration in my own app that is preventing the message composer from displaying correctly?
Finally, after tearing down most of my app, I was able to figure out the issue. Turns out, I was overriding a UIViewController system method in a category (instead of sub-classing):
#implementation UIViewController (UIViewController_Additions)
-(BOOL)canBecomeFirstResponder {
return YES;
}
This has been working fine till iOS7, but something must have changed internally in iOS8 and `MFMessageComposeViewController. A case of the "4-year-old-hack coming to bite you in the ass"
This practice is discouraged by Apple as well, according to this link (though I couldn't find the original Apple source): https://stackoverflow.com/a/14259595/145552
I am trying to use the Google Maps SDK for iOS in a subview of the main view which I created in the storyboard and linked to the view controller via an IBOutlet (I called it extraView, subclassed from UIView). When I follow the steps in the SDK getting started guide, the SDK works just fine, but it uses the uppermost view in the hierarchy (the main view), which I don't want. I want my map to be in a smaller portion of the screen and use the rest of the screen for something else. When I attempt to assign the mapView_ object (see the getting started guide) to self.extraView instead of self.view, the whole screen is black and I get an error in the console output:
"Application windows are expected to have a root view controller at the end of application launch"
Has anyone else figured this out? I can't find anything in the documentation and the sample code Google provides does not use a storyboard.
Here's how...
add a UIView into the view controller where you're working
set it's class to be GMSMapView in the identity inspector.
Then control-drag it to your code as you would for any other outlet.
You can lazily instantiate it in its setter...
- (void) setMapView:(GMSMapView *)mapView {
if (!mapView) {
mapView = [[GMSMapView alloc] initWithFrame:mapView.bounds];
}
_mapView = mapView;
}
To display a map Google's sample code becomes...
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:1.285
longitude:103.848
zoom:12];
self.mapView = [GMSMapView mapWithFrame:CGRectZero camera:camera];
I solved my problem just removing the loadview code that i took from the example.
Just adding a view as sberley said should works.
just on thing more, on the identity inspector, that attribute that you have to change is class, at least it is on xcode 4.5