Apple TV force focus another view - ios

I'm working on Apple TV project. The project contains tab bar view controller, normally the tab bar will be appeared when swiping up on remote and hidden when swiping down. But now I reverse that behavior and I want to force focus another view when swiping up(normally focus on tab bar). Any way to do that? Thank you.

In your UIViewController, override shouldUpdateFocusInContext. If you detect an upward navigation into the tab bar, return false to prevent focus from reaching the tab bar. Then use a combination of preferredFocusEnvironments + setNeedsFocusUpdate to redirect focus somewhere else:
override func shouldUpdateFocus(in context: UIFocusUpdateContext) -> Bool {
if let nextView: UIView = context.nextFocusedView{
if ( context.focusHeading == .up && nextView.isDescendant(of: tabBar) ){
changeFocusTo(myView)
return false
}
}
}
internal var viewToFocus: UIView?
func changeFocusTo(_ view:UIView? ){
viewToFocus = view
setNeedsFocusUpdate()
}
override var preferredFocusEnvironments: [UIFocusEnvironment]{
return viewToFocus != nil ? [viewToFocus!] : super.preferredFocusEnvironments
}
This is a generally useful technique for customizing focus updates. An alternative technique is to use UIFocusGuide. You could insert a focus guide underneath the tab bar or surround the tab bar with a focus guide to redirect focus. Though focus guides are useful for simple cases, I have generally had better results using the technique I am describing instead.

I got the same issue with focus of UITabbarController before and I found the solution in Apple Support
Because UIViewController conforms to UIFocusEnvironment, custom view
controllers in your app can override UIFocusEnvironment delegate
methods to achieve custom focus behaviors. Custom view controllers
can:
Override the preferredFocusedView to specify where focus should start
by default. Override shouldUpdateFocusInContext: to define where focus
is allowed to move. Override
didUpdateFocusInContext:withAnimationCoordinator: to respond to focus
updates when they occur and update your app’s internal state. Your
view controllers can also request that the focus engine reset focus to
the current preferredFocusedView by callingsetNeedsFocusUpdate. Note
that calling setNeedsFocusUpdate only has an effect if the view
controller contains the currently focused view.
For more detail, please check this link
https://developer.apple.com/library/content/documentation/General/Conceptual/AppleTV_PG/WorkingwiththeAppleTVRemote.html#//apple_ref/doc/uid/TP40015241-CH5-SW14

Related

UIButton selector not working after button tapped within WKWebView

I have a custom titleView with 2 custom UIButtons with arrow images that allow navigation to the next view controller in the paging structure. They work perfectly fine until a button is tapped within the WKWebView. Then they don't work anymore and the selector is not called. Note that other buttons in the nav bar still work (UIBarButtonItems). The buttons work properly again after the user swipes over to the next view controller.
After looking into it some, it looks like a WKCompositingView becomes first responder and if I override becomeFirstResponder() in a WKWebView subclass, the issue goes away. I'm still a little baffled though, and would like to understand the root of the problem.
class NonFirstRespondableWebView: WKWebView {
override func becomeFirstResponder() -> Bool {
return false
}
}
Does anyone have any insight into why this is happening?
Most UI elements in swift have a UIResponder. Unhandled events are passed up the responder chain to enclosing views. My guess is that the WKWebView is absorbing all touch events once the window has become active. You can learn more about the responder chain here
Regarding a first responder. From the docs:
The first responder is usually the first object in a responder chain to receive an event or action message. In most cases, the first responder is a view object that the user selects or activates with the mouse or keyboard.
Assuming you want to keep interactivity with the WKWebView fully functional (e.g. you need to bring up a keyboard or something), you can use
webView.resignFirstResponder()
To resign the responder at any time.
Otherwise, an extension that would give you the same functionality might look something like this:
extension WKWebView {
open override func becomeFirstResponder() -> Bool {
if self.superview?.superview is UIWebView {
return false
} else {
return super.becomeFirstResponder()
}
}
}

Popup From a Tabbar (Like Yelp App)

I am pretty much trying to replicate the same tabBar popup as you see in the yelp app (before and after screenshots at the bottom) where no matter what view your in you can press on the center tabBar item and pop up will appear.
Coincidentally I have 5 tabBar items (like Yelp) and I am trying to have three popups with a image and title for each (like Yelp). Seeing that what I am trying to do is already done in an app shows me that this is possible, but I do not know how to do it. I have tried to change the types of relationships between view controllers or do it programmatically, but nothing seems to work. What am I missing or doing wrong?
Tabbar Controller Code:
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if viewController.tabBarItem.tag == 1 {
return false
} else {
return true
}
}
Before Press
After Press
One solution could be to adopt the UITabBarControllerDelegate.
This way we can use the func tabBarController(UITabBarController, didSelect: UIViewController) method of the delegate to alter the regular presentation behavior and show our popups instead. We could find the offset required (in terms of CGPoint) relative to the tab bar button and then apply that offset and add the popup buttons to as a subview. Note that for this method you would need to programmatically set the frames of your popup buttons.
Alternatively, you could also make a bunch of popup buttons and set their alphas to zero and one when clicked. Hope this helped! Thanks :)
Custom action for tab bar
UITabBarControllerDelegate documentation

How to move focus at button in collectionview cell in tvos application?

I am working on apple TV application. In my app, I have made a screen which has a collection view. In that case, I am able to move focus at collection view cell but not able to move focus to the button which is in collection view cell so can anyone help me to solve this issue ?
please give me an answer if anyone knows this answer.
I am able to solve this problem by adding below methods in collectionViewCell subclass.
In CollectionViewCell Subclass
override var preferredFocusEnvironments: [UIFocusEnvironment]{
// Condition
}
override func shouldUpdateFocus(in context: UIFocusUpdateContext) -> Bool {
// Condition
}
override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
// Condition
}
you can see more at this link:
enter link description here.
I think this page will guide to achieve what you want. https://developer.apple.com/library/content/documentation/General/Conceptual/AppleTV_PG/WorkingwiththeAppleTVRemote.html#//apple_ref/doc/uid/TP40015241-CH5-SW4
It give good explaination of how focus engine will decide which object should get next focus. Below is great explanation step by step at above link.
Here is an example showing how focus might be determined:
The focus engine asks the root window for its preferredFocusedView,
which returns its root view controller’s preferredFocusedView
object.
The root view controller, a tab view controller, returns its
select view controller’s preferredFocusedView object.
The select view controller overrides its preferredFocusedView method to return a specific UIButton instance.
The UIButton instance returns self(the default), and is focusable, so it is chosen by the focus engine as the next focused view.
You have to override the preferredFoucsedView property of your UIView or UIViewController.
override weak var preferredFocusedView: UIView? {
if someCondition {
return theViewYouWant
} else {
return defaultView
}
}
Thanks to Slayter's answer

iOS - How to use a small view in different view controllers in Swift

I have a progress bar (with its own controller). This bar is supposed to be shown in different views depending on which view is visible. As the progress will be same, If possible I don't want to create many progress bar in many views rather I want to use same instance in all these views. Also in that way when I need to change any property of the progress bar it will be reflected commonly, which is required.
Please suggest me how can I use this common view. And also if my strategy is wrong, what would be the better design for such scenarios.
1) Well you have 2 options. You can declare a new Class ViewBox (or whatever name) and then use that inside your code
First View Controller
var box:ViewBox = ViewBox()
When you segue or transition to your next screen, you can have a predefined variable var box:ViewBox!. Then say when you press a button, the button has a function called transition.
//Now setup the transition inside the Storyboard and name the identifier "toThirdViewController"
override func prepareForSegue(segue:UIStoryboardSegue, sender:AnyObject?) {
if(segue.identifier == "toThirdViewController") {
var vc = segue.destinationViewController as! `nextViewController` //The class of your next viewcontroller goes here
vc.box = self.box
}
//Since The SecondViewController doesn't need ViewBox, we don't need it there.
}
where
nextViewController:UIViewController {
var box:ViewBox!
}
Or you could do a much simpler way and that is to look up a UIPageViewController :)

Launch Watch App into middle view

Basically, my app is laid out in the page format and I would like it to launch into the middle of the three pages. There is no way of setting a previous page segue, so I have been trying to do it in code.
I have the main view set to the first view, and I have tried a variety of methods to segue to the middle view as soon as the app is launched.
Here is the two ways I tried:
if segueCheck == true {
self.pushControllerWithName("budget", context: self)
self.presentControllerWithName("budget", context: self)
segueCheck = false
}
The first presents the view, but as a completely separate view, and the second replaces the first view with the middle view.
Does anyone know how I can launch into the middle view and allow the user to swipe left and right of it?
Thanks.
WKInterfaceController's becomeCurrentPage() should be what you're looking for.
Let's create a new class for the center view controller, CenterPageViewController, and change its initWithContext: method as follows
import WatchKit
class CenterPageViewController: WKInterfaceController {
override init(context: AnyObject?) {
super.init(context: context)
super.becomeCurrentPage()
}
}
Now let's set the Custom Class for the middle page in your storyboard to CenterPageViewController
and finally hit run.
You won't be able to get rid of the initial transition from the left page to the center page, but the app will finally begin on the middle page.
Update Swift 3.0
class CenterPageViewController: WKInterfaceController {
override init (){
super.init()
super.becomeCurrentPage()
}
}
This will works...!!!
Thanks
The new way to do this in watchOS 4 and higher is:
WKInterfaceController.reloadRootPageControllers(withNames:
["Controller1" "Controller2", "Controller3"],
contexts: [context1, context2, context3],
orientation: WKPageOrientation.horizontal,
pageIndex: 1)
Now you don't get the annoying animation when using becomeCurrentPage() when you want to start with the middle page.

Resources