我有a small iPhone app,它使用导航控制器显示3个视图(此处为fullscreen):
首先它显示社交网络列表(Facebook,Google +等):
然后它会显示一个OAuth对话框,询问凭据:
并且(之后,在相同的UIWebView
中)获取权限:
最后,它会显示最后一个带有用户详细信息的视图控制器(在真实应用中,这将是菜单,可以启动多人游戏):
这一切都运作良好,但我有一个问题,当用户想要返回并选择另一个社交网络时:
用户触摸后退按钮而不显示第一个视图,显示第二个视图,再次询问OAuth凭据/权限。
我可以在这做什么? Xcode 5.0.2显示了一个非常有限的segues选择 - 推送,模态(我无法使用,因为它模糊了我游戏所需的导航栏)和定制
我是一名iOS编程新手,但早些时候我开发了一个Adobe AIR mobile app,可以1)替换视图而不是推送,2)从导航堆栈中删除不需要的视图。
如何在原生应用中做同样的事情?
答案 0 :(得分:56)
为了扩展上面的各种细分,这是我的解决方案。它具有以下优点:
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];
}
答案 1 :(得分:37)
您可以使用自定义segue:为此,您需要创建一个子类化UIStoryboardSegue(示例MyCustomSegue)的类,然后您可以使用类似的东西覆盖“perform”
-(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];
}
此时转到Interface Builder,选择“custom”segue,并输入您的类名称(示例MyCustomSegue)
答案 2 :(得分:22)
自定义segue对我不起作用,因为我有一个Splash视图控制器,我想替换它。由于列表中只有一个视图控制器,popToRootViewController
仍然将Splash留在堆栈中。我使用以下代码替换单个控制器
-(void)perform {
UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
UIViewController *destinationController = (UIViewController*)[self destinationViewController];
UINavigationController *navigationController = sourceViewController.navigationController;
[navigationController setViewControllers:@[destinationController] animated:YES];
}
现在在Swift 4中:
class ReplaceSegue: UIStoryboardSegue {
override func perform() {
source.navigationController?.setViewControllers([destination], animated: true)
}
}
现在在Swift 2.0中
class ReplaceSegue: UIStoryboardSegue {
override func perform() {
sourceViewController.navigationController?.setViewControllers([destinationViewController], animated: true)
}
}
答案 3 :(得分:17)
快速2版的ima747回答:
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);
}
正如他所提到的,它具有以下优点:
答案 4 :(得分:15)
对于这个问题,我认为答案很简单
然后将ViewControllers数组设置回navigationController 如下:
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!
}
与 Swift 3完美配合。
希望它对一些新人有所帮助。
干杯。
答案 5 :(得分:9)
使用展开segue将是解决此问题的最佳方法。我同意劳罗。
以下是从detailsViewController [或viewController3]到myAuthViewController [或viewController1]
设置展开segue的简要说明这实际上就是你如何通过代码执行unwind segue。
在要展开的viewController中实现IBAction方法(在本例中为viewController1)。方法名称可以是任何长的,它需要一个类型为UIStoryboardSegue的参数。
@IBAction func unwindToMyAuth(segue: UIStoryboardSegue) {
println("segue with ID: %@", segue.Identifier)
}
在想要展开的viewController(3)中链接此方法。要进行链接,请右键单击(双指点击)viewController顶部的退出图标,此时' unwindToMyAuth'方法将显示在弹出框中。控制单击此方法到第一个图标,viewController图标(也出现在viewController的顶部,与退出图标位于同一行)。选择'手册'弹出选项。
在文档大纲中,对于同一视图(viewController3),选择刚刚创建的展开segue。转到属性检查器并为此展开segue分配唯一标识符。我们现在有一个通用的展开segue准备使用。
现在,unwind segue可以像代码中的任何其他segue一样执行。
performSegueWithIdentifier("unwind.to.myauth", sender: nil)
这种方法将使您从viewController3转到viewController1,而无需从导航层次结构中删除viewController2。
与其他segue不同,展开segue不会实例化视图控制器,它们只会转到导航层次结构中的现有视图控制器。
答案 6 :(得分:4)
如前所述,流行音乐不是动画,然后推动动画片看起来非常好,因为用户会看到实际的过程。 我建议你先推动动画,然后删除以前的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)
}
}
}
答案 7 :(得分:2)
使用下面的代码最后一个视图控制器 您可以使用其他按钮或将其设置为您自己使用的取消按钮
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES];
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(dismiss:)];
self.navigationItemSetting.leftBarButtonItem = cancelButton;
}
- (IBAction)dismissSettings:(id)sender
{
// your logout code for social media selected
[self.navigationController popToRootViewControllerAnimated:YES];
}
答案 8 :(得分:2)
你真正应该做的是以模态方式呈现UINavigationController
包含社交网络UIViewControllers
覆盖你的菜单UIViewController
(如果你想要的话可以嵌入UINavigationController
)。然后,一旦用户通过身份验证,您就会关闭社交网络UINavigationController
,再次显示您的菜单UIViewController
。
答案 9 :(得分:2)
在swift3
中创建一个segue
-add标识符
-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)
}
}
- 您还必须覆盖源视图控制器中的一个方法
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
return false
}
答案 10 :(得分:1)
答案 11 :(得分:1)
这在Swift 3中对我有用:
class ReplaceSegue: UIStoryboardSegue {
override func perform() {
if let navVC = source.navigationController {
navVC.pushViewController(destination, animated: true)
} else {
super.perform()
}
}
}
答案 12 :(得分:1)
这个怎么样:)我现在是个老问题了,但是这很有魅力:
UIViewController *destinationController = [[UIViewController alloc] init];
UINavigationController *newNavigation = [[UINavigationController alloc] init];
[newNavigation setViewControllers:@[destinationController]];
[[[UIApplication sharedApplication] delegate] window].rootViewController = newNavigation;
答案 13 :(得分:1)
这里有一个快速的Swift 4/5解决方案,方法是创建一个自定义序列,用新的(没有动画)替换(顶部堆栈)viewcontroller:
class SegueNavigationReplaceTop: UIStoryboardSegue {
override func perform () {
guard let navigationController = source.navigationController else { return }
navigationController.popViewController(animated: false)
navigationController.pushViewController(destination, animated: false)
}
}