ios 而不是 push segue 如何替换视图控制器(或从导航堆栈中删除)?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21414786/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Instead of push segue how to replace view controller (or remove from navigation stack)?
提问by Alexander Farber
I have a small iPhone app, which uses a navigation controller to display 3 views (here fullscreen):
我有一个小型 iPhone 应用程序,它使用导航控制器显示 3 个视图(此处为全屏):
First it displays a list of social networks (Facebook, Google+, etc.):
首先它显示社交网络列表(Facebook、Google+ 等):
Then it displays an OAuth dialog asking for credentials:
然后它会显示一个 OAuth 对话框,要求提供凭据:
And (after that, in same UIWebView
) for permissions:
和(之后,在相同的UIWebView
)权限:
Finally it displays the last view controller with user details (in the real app this will be the menu, where the multiplayer game can be started):
最后,它显示带有用户详细信息的最后一个视图控制器(在实际应用程序中,这将是菜单,可以在其中启动多人游戏):
This all works well, but I have a problem, when the user wants to go back and select another social network:
这一切都很好,但我有一个问题,当用户想要返回并选择另一个社交网络时:
The user touches the back button and instead of being displayed the first view, is displayed the second one, asking for OAuth credentials/permissions again.
用户触摸后退按钮,而不是显示第一个视图,而是显示第二个视图,再次请求 OAuth 凭据/权限。
What can I do here? Xcode 5.0.2 shows a very limited choice for segues - push, modal(which I can't use, because it obscures navigation bar needed for my game) and custom.
我能在这里做什么?Xcode 5.0.2 显示了一个非常有限的 segues 选择 - push、modal(我不能使用,因为它掩盖了我的游戏所需的导航栏)和custom。
I am an iOS programming newbie, but earlier I have developed an Adobe AIR mobile appand there it was possible to 1) replace view instead of pushing and 2) remove an unneeded view from navigation stack.
我是 iOS 编程新手,但早些时候我开发了一个Adobe AIR 移动应用程序,并且可以 1) 替换视图而不是推送和 2) 从导航堆栈中删除不需要的视图。
How to do the same in a native app please?
请如何在本机应用程序中执行相同操作?
采纳答案by Daniele
You could use a custom segue: to do it you need to create a class subclassing UIStoryboardSegue (example MyCustomSegue), and then you can override the "perform" with something like this
您可以使用自定义 segue:为此,您需要创建一个继承 UIStoryboardSegue 的类(例如 MyCustomSegue),然后您可以使用类似的内容覆盖“执行”
-(void)perform {
UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
UIViewController *destinationController = (UIViewController*)[self destinationViewController];
UINavigationController *navigationController = sourceViewController.navigationController;
// Pop to root view controller (not animated) before pushing
[navigationController popToRootViewControllerAnimated:NO];
[navigationController pushViewController:destinationController animated:YES];
}
At this point go to Interface Builder, select "custom" segue, and put the name of your class (example MyCustomSegue)
此时转到Interface Builder,选择“自定义”segue,并输入您的类的名称(例如MyCustomSegue)
回答by ima747
To expand on the various segues above, this is my solution. It has the following advantages:
为了扩展上面的各种segue,这是我的解决方案。它具有以下优点:
- Can work anywhere in the view stack, not just the top view (not sure if this is realistically ever needed or even technically possible to trigger, but hey it's in there).
- It doesn't cause a pop OR transition to the previous view controller before displaying the replacement, it just displays the new controller with a natural transition, with the back navigation being to the same back navigation of the source controller.
- 可以在视图堆栈中的任何位置工作,而不仅仅是顶视图(不确定这是否真的需要,甚至在技术上是否可以触发,但嘿它在那里)。
- 在显示替换之前,它不会导致弹出或过渡到前一个视图控制器,它只是显示具有自然过渡的新控制器,后退导航与源控制器的后退导航相同。
Segue Code:
转场代码:
- (void)perform {
// Grab Variables for readability
UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
UIViewController *destinationController = (UIViewController*)[self destinationViewController];
UINavigationController *navigationController = sourceViewController.navigationController;
// Get a changeable copy of the stack
NSMutableArray *controllerStack = [NSMutableArray arrayWithArray:navigationController.viewControllers];
// Replace the source controller with the destination controller, wherever the source may be
[controllerStack replaceObjectAtIndex:[controllerStack indexOfObject:sourceViewController] withObject:destinationController];
// Assign the updated stack with animation
[navigationController setViewControllers:controllerStack animated:YES];
}
回答by christophercotton
The custom segue didn't work for me, as I had a Splash view controller and I wanted to replace it. Since there was just one view controller in the list, the popToRootViewController
still left the Splash on the stack. I used the following code to replace the single controller
自定义转场对我不起作用,因为我有一个 Splash 视图控制器,我想替换它。由于列表中只有一个视图控制器,因此popToRootViewController
仍然将 Splash 留在堆栈中。我使用以下代码替换了单个控制器
-(void)perform {
UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
UIViewController *destinationController = (UIViewController*)[self destinationViewController];
UINavigationController *navigationController = sourceViewController.navigationController;
[navigationController setViewControllers:@[destinationController] animated:YES];
}
and now in Swift 4:
现在在 Swift 4 中:
class ReplaceSegue: UIStoryboardSegue {
override func perform() {
source.navigationController?.setViewControllers([destination], animated: true)
}
}
and now in Swift 2.0
现在在 Swift 2.0
class ReplaceSegue: UIStoryboardSegue {
override func perform() {
sourceViewController.navigationController?.setViewControllers([destinationViewController], animated: true)
}
}
回答by Dr.Agos
the swift 2 version of ima747 answer:
ima747 的 swift 2 版本回答:
override func perform() {
let navigationController: UINavigationController = sourceViewController.navigationController!;
var controllerStack = navigationController.viewControllers;
let index = controllerStack.indexOf(sourceViewController);
controllerStack[index!] = destinationViewController
navigationController.setViewControllers(controllerStack, animated: true);
}
As he mentioned it has the following advantages:
正如他所说,它具有以下优点:
- Can work anywhere in the view stack, not just the top view (not sure if this is realistically ever needed or even technically possible to trigger, but hey it's in there).
- It doesn't cause a pop OR transition to the previous view controller before displaying the replacement, it just displays the new controller with a natural transition, with the back navigation being to the same back navigation of the source controller.
- 可以在视图堆栈中的任何位置工作,而不仅仅是顶视图(不确定这是否真的需要,甚至在技术上是否可以触发,但嘿它在那里)。
- 在显示替换之前,它不会导致弹出或过渡到前一个视图控制器,它只是显示具有自然过渡的新控制器,后退导航与源控制器的后退导航相同。
回答by Xuan-Gieng Nguyen
For this problem, I think the answer is just simple as
对于这个问题,我认为答案很简单
- Get the array of view controllers from NavigationController
- Removing the last ViewController (current view controller)
- Insert a new one at last
Then set the array of ViewControllers back to the navigationController as bellow:
if let navController = self.navigationController { let newVC = DestinationViewController(nibName: "DestinationViewController", bundle: nil) var stack = navController.viewControllers stack.remove(at: stack.count - 1) // remove current VC stack.insert(newVC, at: stack.count) // add the new one navController.setViewControllers(stack, animated: true) // boom! }
- 从 NavigationController 获取视图控制器数组
- 删除最后一个 ViewController(当前视图控制器)
- 最后插入一个新的
然后将 ViewControllers 数组设置回导航控制器,如下所示:
if let navController = self.navigationController { let newVC = DestinationViewController(nibName: "DestinationViewController", bundle: nil) var stack = navController.viewControllers stack.remove(at: stack.count - 1) // remove current VC stack.insert(newVC, at: stack.count) // add the new one navController.setViewControllers(stack, animated: true) // boom! }
works perfectly with Swift 3.
与Swift 3完美配合。
Hope it helps for some new guys.
希望对一些新人有所帮助。
Cheers.
干杯。
回答by Lester
Using unwind segue would be the most appropriate solution to this problem. I agree with Lauro.
使用 unwind segue 将是解决这个问题的最合适的方法。我同意劳罗的观点。
Here is a brief explanation to setup an unwind segue from detailsViewController[or viewController3] to myAuthViewController[or viewController1]
以下是设置从 detailsViewController[或 viewController3] 到 myAuthViewController[或 viewController1] 的展开转场的简要说明
This is essentially how you would go about performing an unwind segue through the code.
这基本上是您通过代码执行展开转场的方式。
Implement an IBAction method in the viewController you want to unwind to(in this case viewController1). The method name can be anything so long that it takes one argument of the type UIStoryboardSegue.
@IBAction func unwindToMyAuth(segue: UIStoryboardSegue) { println("segue with ID: %@", segue.Identifier) }
Link this method in the viewController(3) you want to unwind from. To link, right click(double finger tap) on the exit icon at the top of the viewController, at this point 'unwindToMyAuth' method will show in the pop up box. Control click from this method to the first icon, the viewController icon(also present at the top of the viewController, in the same row as the exit icon). Select the 'manual' option that pops up.
In the Document outline, for the same view(viewController3), select the unwind segue you just created. Go to the attributed inspector and assign a unique identifier for this unwind segue. We now have a generic unwind segue ready to be used.
Now, the unwind segue can be performed just like any other segue from the code.
performSegueWithIdentifier("unwind.to.myauth", sender: nil)
在要展开到的视图控制器中实现一个 IBAction 方法(在本例中为 viewController1)。方法名称可以是任何长度,只要它需要一个 UIStoryboardSegue 类型的参数。
@IBAction func unwindToMyAuth(segue: UIStoryboardSegue) { println("segue with ID: %@", segue.Identifier) }
将此方法链接到要从中放松的 viewController(3) 中。要链接,请右键单击(双指点击)viewController 顶部的退出图标,此时'unwindToMyAuth' 方法将显示在弹出框中。控制从该方法单击到第一个图标,即 viewController 图标(也出现在 viewController 的顶部,与退出图标在同一行)。选择弹出的“手动”选项。
在 Document 大纲中,对于同一个视图 (viewController3),选择您刚刚创建的 unwind segue。转到属性检查器并为此展开转场分配唯一标识符。我们现在有一个通用的 unwind segue 可以使用了。
现在,展开转场可以像代码中的任何其他转场一样执行。
performSegueWithIdentifier("unwind.to.myauth", sender: nil)
This approach, will take you from viewController3 to viewController1 without the need to remove viewController2 from the navigation hierarchy.
这种方法会将您从 viewController3 带到 viewController1,而无需从导航层次结构中删除 viewController2。
Unlike other segues, unwind segues do not instantiate a view controller, they only go to an existing view controller in the navigation hierarchy.
与其他 segue 不同,unwind segue 不会实例化视图控制器,它们只会转到导航层次结构中的现有视图控制器。
回答by Alex Shubin
As mentioned in previous answers to pop not animated and then to push animated won't look very good because user will see the actual process. I recommend you first push animated and then remove the previous vc. Like so:
正如之前的回答中提到的那样,弹出不动画然后推送动画看起来不太好,因为用户会看到实际的过程。我建议你先推送动画,然后删除之前的vc。像这样:
extension UINavigationController {
func replaceCurrentViewController(with viewController: UIViewController, animated: Bool) {
pushViewController(viewController, animated: animated)
let indexToRemove = viewControllers.count - 2
if indexToRemove >= 0 {
viewControllers.remove(at: indexToRemove)
}
}
}
回答by HixField
Here a quick Swift 4/5 solution by creating a custom seque that replaces the (top stack) viewcontroller with the new one (without animation) :
这是一个快速的 Swift 4/5 解决方案,通过创建一个自定义序列,用新的(无动画)替换(顶部堆栈)视图控制器:
class SegueNavigationReplaceTop: UIStoryboardSegue {
override func perform () {
guard let navigationController = source.navigationController else { return }
navigationController.popViewController(animated: false)
navigationController.pushViewController(destination, animated: false)
}
}
回答by Mahesh Giri
In swift3
create one segue
-add identifier
-add and set in segue(storyboard) custom storyboard class from cocoatouch file
-In custom class override perform()
在swift3
创建一个 segue -add identifier -add 并从 cocoatouch 文件中设置 segue(storyboard) 自定义故事板类 - 在自定义类覆盖 perform()
override func perform() {
let sourceViewController = self.source
let destinationController = self.destination
let navigationController = sourceViewController.navigationController
// Pop to root view controller (not animated) before pushing
if self.identifier == "your identifier"{
navigationController?.popViewController(animated: false)
navigationController?.pushViewController(destinationController, animated: true)
}
}
-You also have to override one method in your source viewcontroller
- 您还必须在源视图控制器中覆盖一种方法
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
return false
}
回答by Mark
What you should really do is modally present a UINavigationController
containing the social network UIViewControllers
overtop of your Menu UIViewController
(which can be embedded in a UINavigationController
if you want). Then, once a user has authenticated, you dismiss the social network UINavigationController
, showing your Menu UIViewController
again.
您真正应该做的是模态地呈现UINavigationController
包含UIViewControllers
菜单顶部的社交网络UIViewController
(UINavigationController
如果您愿意,可以将其嵌入)。然后,一旦用户通过身份验证,您就会关闭社交网络UINavigationController
,UIViewController
再次显示您的菜单。