视图被合并而不是单独显示

Posted

技术标签:

【中文标题】视图被合并而不是单独显示【英文标题】:views are merged instead of showing them separately 【发布时间】:2018-09-04 05:21:42 【问题描述】:

我有两个 xib 文件,一个显示登录视图,另一个显示登录成功后的步骤。我很难让它发挥作用。我创建了 macos 项目而不是 ios 并使用 safariservices 以便它也适用于 safari 扩展。

这就是我所做的

import SafariServices


class SafariExtensionViewController: SFSafariExtensionViewController 


    @IBOutlet weak var passwordMessage: NSTextField!
    @IBOutlet weak var emailMessage: NSTextField!
    @IBOutlet weak var message: NSTextField!
    @IBOutlet weak var email: NSTextField!
    @IBOutlet weak var password: NSSecureTextField!
    static let shared = SafariExtensionViewController()
    override func viewDidLoad() 
        self.preferredContentSize = NSSize(width: 300, height: 250)
        message.stringValue = ""
        emailMessage.stringValue = ""
        passwordMessage.stringValue = ""
    
    override func viewDidAppear() 
        if let storedEmail = UserDefaults.standard.object(forKey: "email") as? String 
            if let stepView = Bundle.mainBundle.loadNibNamed(NSNib.Name(rawValue: "ExtensionStepsViewController"), owner: nil, topLevelObjects: nil)[0] 
                self.view.addSubview(stepView)
            
        
    

    @IBAction func userLogin(_ sender: Any) 
        let providedEmailAddress = email.stringValue
        let providedPassword = password.stringValue
        let isEmailAddressValid = isValidEmailAddress(emailAddressString: providedEmailAddress)
        self.message.stringValue = ""
        emailMessage.stringValue = ""
        passwordMessage.stringValue = ""
        if isEmailAddressValid && providedPassword.count > 0 
          /* login process is handled here and store the email in local storage /*
          /* TODO for now email is not stored in browser localstorage which has to be fixed */
         let controller = "ExtensionStepsViewController"
         let subview = ExtensionStepsViewController(nibName: NSNib.Name(rawValue: controller), bundle: nil)
         self.view.addSubview(subview.view)
            


这样我会得到像Type Bool has no subscript members 这样的错误,我的文件结构看起来像这样。

SafariExtensionViewController.xib(最初显示的主要 带有登录屏幕)

SafariExtensionViewController.swift

ExtensionStepsViewController.xib(这个视图应该在用户使用时显示 已登录而不是登录屏幕)

ExtensionStepsViewController.swift

我正在使用 xcode 10、swift 4,一切都是新的。

更新

我在 viewDidAppear(如果本地存储中有电子邮件,则显示扩展步骤视图而不是登录屏幕)和登录功能内部都使用了以下块,当登录成功但它没有导航到该 ExtensionStepsView

let controller = "ExtensionStepsViewController"
let subview = ExtensionStepsViewController(nibName: NSNib.Name(rawValue: controller), bundle: nil)
self.view.addSubview(subview.view)

用例是在初始时显示登录,但如果用户已登录,则显示另一个视图,但问题是现在视图已合并

【问题讨论】:

nib 文件中有 UIView 或 UIView 控制器吗? 它来自使用 cocoa 的 NSViewController。 @TushantKhatiwada 你能和我分享你在谷歌驱动器上的例子吗?非常感谢你!!! 【参考方案1】:

您收到错误“Bool 类型没有下标成员”,因为 Bundle 的 loadNibNamed(_:owner:topLevelObjects:) 方法返回没有 subscript 成员的 Bool 结构,因此您不能这样写

true[0]

如何正确使用此方法请参阅link 和那里的示例:

var topLevelObjects : NSArray?
if Bundle.main.loadNibNamed(NSNib.Name(rawValue: "ExtensionStepsViewController"), owner: self, topLevelObjects: &topLevelObjects) 
     let  topLevelObjects!.first(where:  $0 is NSView ) as? NSView

视图已合并,因为您没有从父视图中删除以前的视图,而是将 ExtensionStepsViewController 中的视图添加到同一个父视图中。

您可以执行以下步骤来完成您的问题:

    使 SafariExtensionViewController 继承自 SFSafariExtensionViewController,它将成为两个子视图控制器(例如 LoginViewController 和 ExtensionStepsViewController)的容器(和父级),并将用于在它们之间导航。 分别制作 LoginViewController 和 ExtensionStepsViewController(均继承自简单的NSViewController)及其 xib。 就在用户从 LoginViewController 登录 transit 到 ExtensionStepsViewController 之后

作为示例,但您必须使用您的实现 SafariExtensionViewController 而不是 ParentViewController,正如我在第一步中解释的那样。

public protocol LoginViewControllerDelegate: class 
    func loginViewControllerDidLoginSuccessful(_ loginVC: LoginViewController)


public class LoginViewController: NSViewController 
    weak var delegate: LoginViewControllerDelegate?

    @IBAction func login(_ sender: Any) 
        // login logic
        let isLoginSuccessful = true
        if isLoginSuccessful 
            self.delegate?.loginViewControllerDidLoginSuccessful(self)
        
    


public class ExtensionStepsViewController: NSViewController 



public class ParentViewController: NSViewController, LoginViewControllerDelegate 
    weak var login: LoginViewController! // using of force unwrap is anti-pattern. consider other solutions
    weak var steps: ExtensionStepsViewController!

    public override func viewDidLoad() 
        let login = LoginViewController(nibName: NSNib.Name(rawValue: "LoginViewController"), bundle: nil)
        login.delegate = self
        // change login view frame if needed
        login.view.frame = self.view.frame
        self.view.addSubview(login.view)
        // instead of setting login view frame you can add appropriate layout constraints
        self.addChildViewController(login)
        self.login = login

        let steps = ExtensionStepsViewController(nibName: NSNib.Name(rawValue: "ExtensionStepsViewController"), bundle: nil)
        steps.view.frame = self.view.frame
        self.addChildViewController(steps)
        self.steps = steps
    

    // MARK: - LoginViewControllerDelegate

    public func loginViewControllerDidLoginSuccessful(_ loginVC: LoginViewController) 
        self.transition(from: self.login, to: self.steps, options: .slideLeft) 
            // completion handler logic
            print("transition is done successfully")
        
    

Here 是这个例子的快速游乐场。

UPD:

您可以通过多种方式实例化 NSViewController:

    使用NSStoryboard 允许从 .storyboard 文件加载 NSViewController 的视图:
let storyboard = NSStoryboard(name: NSStoryboard.Name("NameOfStoryboard"), bundle: nil)
let viewController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("NSViewControllerIdentifierInStoryboard"))
    使用适当的initialiser NSViewController 从 .xib 文件加载它的视图:
let steps = ExtensionStepsViewController(nibName: NSNib.Name(rawValue: "ExtensionStepsViewController"), bundle: nil)
    使用默认初始化程序,但如果 xib 文件的名称与视图控制器类的名称不同,则必须通过覆盖 loadView() 方法直接加载视图:
let steps = ExtensionStepsViewController()
// Also you have to override loadView() method of ExtensionStepsViewController.

【讨论】:

你能检查一下吗?我试着按照你的解释和代码做,但不明白 logindelegate 代码应该放在哪里gist.github.com/tushantOffice/46dd0c15434144fb42d23882b07b4f15 LoginViewControllerDelegate 是一种协议,类似于来自其他编程语言的接口。你的 SafariExtensionViewController(在第一步中解释)应该符合这个协议并实现它的所有方法。 LoginUIController & ExtensionStepsViewController 可能继承自简单的 NSViewController。 见Delegation pattern 我更新了要点,我这样做了,在login.delegate = self 行中出现错误,它说Cannot assign value of type 'SafariExtensionViewController' to type 'LoginViewControllerDelegate?'

以上是关于视图被合并而不是单独显示的主要内容,如果未能解决你的问题,请参考以下文章

将多个视图合并到自定义视图中

单独的相机类或合并在一起?

如何制作像下面这样的视图而不是使用 DataGridView?

如何将两个单独的 javascript 合并为一个?

CoreData 合并冲突显示托管对象版本更改而不是数据

使用topomerge合并topojson会打乱缠绕顺序