Swift的Facebook登录按钮

12

如果我在 Xcode 中创建了一个 UIView ,然后将自定义类添加为 FBSDKLoginButton,当我点击它时,它会引导我通过 Facebook 登录,然后返回到与 FBSDKLoginButton 相同的页面,但不再显示登录按钮,而是显示“现在注销”。我该如何让它在单击登录按钮时跳转到新视图?


我通过 CocoaPods 下载了 Facebook SDK,并且这是我第一次使用它,所以我对此感到困惑。感谢您的帮助!


你应该在视图中检查成功回调(即登录成功)。如果已经登录,可以“重定向”到另一个视图。 - Edwin Lambregts
这可能是一个愚蠢的问题,但是既然视图控制器中没有实际的代码,我该如何处理这个问题? - Soporificdreamer
5个回答

28
一种选项是将您的视图控制器设置为FBSDKLoginButton的代理,并实现loginButton:didCompleteWithResult:error:方法,该方法在使用按钮登录时调用。

Swift

class ViewController: UIViewController, FBSDKLoginButtonDelegate {

    @IBOutlet weak var loginButton: FBSDKLoginButton!        

    override func viewDidLoad() {
        super.viewDidLoad()

        self.loginButton.delegate = self
    }
}

Objective-C
// ViewController.h
@interface ViewController : UIViewController <FBSDKLoginButtonDelegate>

@property (weak, nonatomic) IBOutlet FBSDKLoginButton *loginButton;

@end

// ViewController.m
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.loginButton.delegate = self;
}

然后,在loginButton:didCompleteWithResult:error:方法中,您可以检查resulterror,如果一切正常,导航到另一个视图。 Swift
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!) {
        if ((error) != nil) {
            // Process error
        }
        else if result.isCancelled {
            // Handle cancellations
        }
        else {
            // Navigate to other view
        }   
    }

Objective-C
// ViewController.m
@implementation ViewController

- (void)loginButton:(FBSDKLoginButton *)loginButton 
  didCompleteWithResult:(FBSDKLoginManagerLoginResult *)result
                  error:(NSError *)error {
    if (error) {
        // Process error
    }
    else if (result.isCancelled) {
       // Handle cancellations
    }
    else {
        // Navigate to other view
    }
}

你可以在他们的文档中了解更多关于如何使用Facebook登录的信息。

6
在Swift中,这会是这样的:
class MyViewController: UIViewController, FBSDKLoginButtonDelegate {
    @IBOutlet weak var loginView : FBSDKLoginButton!
    @IBOutlet weak var profilePictureView : FBSDKProfilePictureView!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.loginView.delegate = self

        if (FBSDKAccessToken.currentAccessToken() != nil)
        {
            performSegueWithIdentifier("unwindToViewOtherController", sender: self) 
        }
        else
        {
            loginView.readPermissions = ["public_profile", "email", "user_friends"]
        }

    }

    func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!) {
        println("User Logged In")

        if ((error) != nil)
        {
            // Process error
        }
        else if result.isCancelled {
            // Handle cancellations
        }
        else {
            // If you ask for multiple permissions at once, you
            // should check if specific permissions missing
            if result.grantedPermissions.contains("email")
            {
                // Do work
            }
        }
    }

    func loginButtonDidLogOut(loginButton: FBSDKLoginButton!) {
        println("User Logged Out")
    }
}

然后,在您的TargetViewController中添加一个取消操作函数:

@IBAction func unwindToViewOtherController(segue:UIStoryboardSegue) {
    }

1
谢谢伙计,那段代码片段非常有用! - Nate Uni
如果 if (FBSDKAccessToken.currentAccessToken() != nil) 里面的 performSegueWithIdentifier("unwindToViewOtherController", sender: self) 没有加载下一个视图控制器,请问可以帮忙解决吗? - rAzOr
在故事板中选择“取消转场”然后选择属性检查器。在那里,您应该有 unwindToViewOtherController 作为 标识符unwindToViewOtherController: 作为 操作 - Ruud Kalis
我不得不添加 viewDidAppear 函数并在其中编写 performSegueWithIdentifier。这解决了问题。我们不能在 viewDidLoad 函数中使用 performSegueWithIdentifier,这是我从谷歌学到的。请问您能帮我知道为什么吗? - rAzOr
你不能关闭一个还没有加载的视图。ViewDidLoad函数执行完毕后,视图才会被呈现出来,因此才能被关闭。 - Ruud Kalis

2
在当前Swift版本的FacebookLogin(0.2.0)中,LoginButton代理属性被定义为强引用属性:
public class LoginButton: UIView {
...
  /// Delegate of the login button that can handle the result, logout events.
public var delegate: LoginButtonDelegate?
... }

如果您按照Facebook的说明添加登录按钮,并将您的 UIViewController 子类设置为按钮代理...
import FacebookLogin

func viewDidLoad() {
    let loginButton = LoginButton(readPermissions: [ .PublicProfile ])
    loginButton.center = view.center
    loginButton.delegate = self
    view.addSubview(loginButton)
}

如果不小心出现循环引用,就会创建一个引用循环。视图将包含对按钮的强引用,按钮将包含对控制器的强引用,控制器将对其视图保持强引用,请参见此帖子

我的解决方案是使用一个弱成员变量来引用登录按钮,当视图消失时,将按钮委托设置为nil,如下所示:

import UIKit
import FacebookCore
import FacebookLogin
import RxSwift

class LoginViewController: UIViewController, LoginButtonDelegate {

    private weak var facebookLoginButton: LoginButton? = nil

    override func viewDidLoad() {

        super.viewDidLoad()

        // Add the Facebook login button
        let loginButton = LoginButton(readPermissions: [ .publicProfile, .email, .userFriends ])
        loginButton.center = view.center
        // WARNING!: Facebook login button delegate property is defined currently as STRONG.
        // Therefore, it must be set to nil before leaving the view to avoid reference cycles
        loginButton.delegate = self
        view.addSubview(loginButton)
        // Store the login button as a weak reference, since it is holded by the main view with a
        // strong reference
        facebookLoginButton = loginButton
    }

    override func willMove(toParentViewController parent: UIViewController?) {
        super.willMove(toParentViewController:parent)
        if parent == nil {
            // The back button was pressed, interactive gesture used, or programatically pop view
            // was executed
            // Do not forget to set delegate in Facebook button to nil to break reference cycle.
            facebookLoginButton?.delegate = nil
        }
    }

    // MARK: - Facebook login

    /**
     Called when the button was used to login and the process finished.

     - parameter loginButton: Button that was used to login.
     - parameter result:      The result of the login.
     */
    func loginButtonDidCompleteLogin(_ loginButton: LoginButton, result: LoginResult) {

        switch result {
            case .failed(let error):
                // Action on failed
            case .cancelled:
                // Action on cancelled
            case .success(let grantedPermissions, let declinedPermissions, let accessToken):
                // Action on success
        }
    }

    /**
     Called when the button was used to logout.

     - parameter loginButton: Button that was used to logout.
     */
    func loginButtonDidLogOut(_ loginButton: LoginButton) {

        // Action on logout
    }
}

不要使用函数viewWillDissapear()来设置委托为nil,因为Facebook登录页面将显示在您的应用程序顶部,触发此功能,并且您将不再是委托,因此将无法获取登录结果。 请注意,此解决方案适用于导航控制器中的视图。 对于模态窗口,应找到另一种解决方案。
希望这能有所帮助, Xavi

2
如果你删掉了那些说“这是一个注释”的行……对我来说,这实际上看起来像是一个答案;-) - GhostCat

2

IOS 13使用场景委托(Scene Delegate)。只需将下面的代码粘贴到场景委托中,简单调用Facebook登录管理器,它将返回Facebook用户对象。此函数会自动调用。

 func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        guard let url = URLContexts.first?.url else {
            return
        }
        let _ = ApplicationDelegate.shared.application(
            UIApplication.shared,
            open: url,
            sourceApplication: nil,
            annotation: [UIApplication.OpenURLOptionsKey.annotation])
    }

0

你可以像appcoda的教程中所示这样做(见下面的代码)

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"Facebook Profile";

    // Check if user is cached and linked to Facebook, if so, bypass login    
    if ([PFUser currentUser] && [PFFacebookUtils isLinkedWithUser:[PFUser currentUser]]) {
        [self.navigationController pushViewController:  [[UserDetailsViewController alloc] initWithStyle:UITableViewStyleGrouped] animated:NO];
    }

}


#pragma mark - Login methods

/* Login to facebook method */

- (IBAction)loginButtonTouchHandler:(id)sender  {
    // Set permissions required from the facebook user account
    NSArray *permissionsArray = @[ @"user_about_me", @"user_relationships", @"user_birthday", @"user_location"];

// Login PFUser using facebook
[PFFacebookUtils logInWithPermissions:permissionsArray block:^(PFUser *user, NSError *error) {
    [_activityIndicator stopAnimating]; // Hide loading indicator

    if (!user) {
        if (!error) {
            NSLog(@"Uh oh. The user cancelled the Facebook login.");
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Log In Error" message:@"Uh oh. The user cancelled the Facebook login." delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Dismiss", nil];
            [alert show];
        } else {
            NSLog(@"Uh oh. An error occurred: %@", error);
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Log In Error" message:[error description] delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Dismiss", nil];
            [alert show];
        }
    } else if (user.isNew) {
        NSLog(@"User with facebook signed up and logged in!");
        [self.navigationController pushViewController:[[UserDetailsViewController alloc] initWithStyle:UITableViewStyleGrouped] animated:YES];
    } else {
        NSLog(@"User with facebook logged in!");
        [self.navigationController pushViewController:[[UserDetailsViewController alloc] initWithStyle:UITableViewStyleGrouped] animated:YES];
         }
    }];

    [_activityIndicator startAnimating]; // Show loading indicator until login is finished
}

这里有一个演示应用程序


1
这个问题被标记为仅链接答案,因此出现在我的审核队列中。通常情况下,这种答案并不被认为是好的。我看到你已经回复了关于仅链接答案的评论。我进行了一些格式和错别字的编辑来整理一下。 - J Richard Snape

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接