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.
Related
I've created a ViewController containing a user button, which is going to be present in several View Controllers in my application.
I'm adding this ViewController dynamically to the needed ViewControllers. The user button is shown, but it's not clickable. What am I doing wrong?
I've tried setting constraints to the view containing the button, setting the container view's frame, disabling user interaction in the container view (not in the button) and nothing seems to work
import UIKit
class ModulePageViewController: UIPageViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.addSharedButtonsSubView()
}
func addSharedButtonsSubView() {
let sharedButtons = storyboard?.instantiateViewController(withIdentifier: sharedButtonsViewControllerName)
view.addSubview((sharedButtons?.view)!)
sharedButtons?.view.frame = CGRect(x: view.frame.minX, y: view.frame.minY, width: view.frame.width, height: view.frame.height)
addChild(sharedButtons!)
sharedButtons?.didMove(toParent: self)
}
}
You can create a custom view (not ViewController) containing the button and just use it where you need in you app.
#LeCalore ...
I would recommend if you want to use a button or any more stuff on multiple View Controllers then you should just make a new ViewController with that button and whatever else you want on it then use it where ever you want.
ViewController -> Present As Pop Over (Presentation : Over Current Context)
I think that's a better approach atleast for starters.
Else, as user said ... you can make a custom view programatically and call it wherever you need that's another approach but it might give you a bit of trouble.
Open to others view if there's one better.
Gluck
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
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
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 :)
This question is mostly about how I should structure my app. When the app loads the user can select from a list of videos to play, however the actual video player is on a different view controller. At the moment I am just keeping that view controller in memory so that the video can play continuously while the user is navigating throughout the app.
So the problems is that if the user selects a video before loading that movie view controller, nothing will happen of course.
How should I structure my app so that the video can play continuously whether or not the movie player view controller is held in memory? Is this possible?
I'm not 100% sure what you're trying to do; but a default project created in XCode will "Programatically load view on app didFinishingLaunching" without any further coding needed.
Generally you should structure your app with an initial view or view controller (like a navigation controller); and start any visible actions in the "viewDidLoad" method of the target view.
In your case, I would recommend loading straight to the video controller first; and then programmatically segueing to the video selector view if no video is selected.
Usually, I create a structure like this in my projects, I think you need to take a special look in OverlayViewController, check out a example:
MainViewController - As rootViewController
|- content (Any view controller to be presented, yes inside MainViewController)
|- OverlayViewController (a view controller over content )
This structure allows you to change contents without change entire hierarchy.
At MainViewController you will need a method to change current content to another, there is a simple example of how can you do that:
// PS I'm not sure if this will work in the first try, I wrote from my mind right now :)
func changeViewController (controller: UIViewController) {
let from: UIViewController
if childViewControllers.count > 0 {
from = childViewControllers.first as! UIViewController
}
else {
presentViewController(controller)
return
}
let transitionContext = SomeViewControllerContextTransitioning (
fromViewController: from,
toViewController: controller
)
transitionContext.onAnimationComplete = { success in
if !success {
// TODO: Error fallback
}
else {
from.view.removeFromSuperview()
from.removeFromParentViewController()
controller.didMoveToParentViewController(self)
}
}
from.willMoveToParentViewController(nil)
addChildViewController(controller)
SomeAnimator().animateTransition(transitionContext)
}
And finally, in your MainViewController you can observe or control your video playback.
I hope this can help you.