1

I started working on this App in Objective-C. Through some problems I had recently, I started using swift for some Features. Everything is working fine. Now I started building a new Feature and decided to do it in swift. I wrote the code in a Swift only Project, for testing. Everything is working fine in the test version, but when implementing it in my main Project I faced a Problem.

The Problem is that I set the view options in the delegate file like this:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    window = UIWindow(frame: UIScreen.main.bounds)
    window?.makeKeyAndVisible()

    let layout = UICollectionViewFlowLayout()
    window?.rootViewController = UINavigationController(rootViewController: HomeController(collectionViewLayout: layout))

    return true
}

But because my main Project delegate file is in Objective-C I dont know how to get this to work in my Swift file in the Objective-C Project. I tried setting the view in the viewDidLaunch file. But that doesnt work. So i am asking myself if it is actually possible to set the code for my swift file in the Objective-C delegate method in objective-c. But for my Project I would like to set the view options inside the swift file. So this is what I tried so far:

import UIKit

class HomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout {

var window: UIWindow?

override func viewDidLoad() {
    super.viewDidLoad()

    window = UIWindow(frame: UIScreen.main.bounds)
    window?.makeKeyAndVisible()

    let layout = UICollectionViewFlowLayout()
    window?.rootViewController = UINavigationController(rootViewController: HomeController(collectionViewLayout: layout))

    navigationItem.title = "Home"

    collectionView?.backgroundColor = UIColor.white

    collectionView?.register(VideoCell.self, forCellWithReuseIdentifier: "cellId")
}

//number of items
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 10
}

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath)

    return cell
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: view.frame.width, height: 200)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 0
}
}

class VideoCell: UICollectionViewCell{
override init(frame:CGRect){
    super.init(frame: frame)
    setupViews()
}

let thumbnailImageView: UIImageView = {
    let imageView = UIImageView()
    imageView.backgroundColor = UIColor.blue
    return imageView
}()

let userProfileImageView: UIImageView = {
    let imageView = UIImageView ()
    imageView.backgroundColor = UIColor.green
    return imageView
}()

let separatorView: UIView = {
    let view = UIView()
    view.backgroundColor = UIColor.black
    return view
}()

let titleLabel: UILabel = {
    let label = UILabel()
    label.backgroundColor = UIColor.purple
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()

let subtitleTextView: UITextView = {
    let textView = UITextView()
    textView.backgroundColor = UIColor.red
    textView.translatesAutoresizingMaskIntoConstraints = false
    return textView
}()

func setupViews(){
    addSubview(thumbnailImageView)
    addSubview(separatorView)
    addSubview(userProfileImageView)
    addSubview(titleLabel)
    addSubview(subtitleTextView)

    //Abstand zum Bildschirmrand (Blau)
    addConstraintsWithFormat(format: "H:|-16-[v0]-16-|", views: thumbnailImageView)

    //Grün
    addConstraintsWithFormat(format: "H:|-16-[v0(42)]", views: userProfileImageView)

    //vertical constraints / v0 = Blau höhe / v1 = Grün höhe / v2 = Linie höhe
    addConstraintsWithFormat(format: "V:|-32-[v0(75)]-8-[v1(44)]-16-[v2(1)]|", views: thumbnailImageView, userProfileImageView, separatorView)

    //Abtrennung zwischen Zellen /zweite Zeile wird in "Große Fläche" umgesetzt
    addConstraintsWithFormat(format: "H:|[v0]|", views: separatorView)

    //top constraint
    addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .top, relatedBy: .equal, toItem: thumbnailImageView, attribute: .bottom, multiplier: 1, constant: 8))
    //left constraint
    addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .left, relatedBy: .equal, toItem: userProfileImageView, attribute: .right, multiplier: 1, constant: 8))
    //right constraint
    addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .right, relatedBy: .equal, toItem: thumbnailImageView, attribute: .right, multiplier: 1, constant: 0))
    //height constraint
    addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .height, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 0, constant: 20))


    //top constraint
    addConstraint(NSLayoutConstraint(item: subtitleTextView, attribute: .top, relatedBy: .equal, toItem: titleLabel, attribute: .bottom, multiplier: 1, constant: 4))
    //left constraint
    addConstraint(NSLayoutConstraint(item: subtitleTextView, attribute: .left, relatedBy: .equal, toItem: userProfileImageView, attribute: .right, multiplier: 1, constant: 8))
    //right constraint
    addConstraint(NSLayoutConstraint(item: subtitleTextView, attribute: .right, relatedBy: .equal, toItem: thumbnailImageView, attribute: .right, multiplier: 1, constant: 0))
    //height constraint
    addConstraint(NSLayoutConstraint(item: subtitleTextView, attribute: .height, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 0, constant: 20))

    thumbnailImageView.frame = CGRect(x: 0, y: 0, width: 50, height: 50)

}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}
}

extension UIView {
func addConstraintsWithFormat(format: String, views: UIView...){
    var viewsDictionary = [String: UIView]()
    for (index, view) in views.enumerated(){
        let key = "v\(index)"
        view.translatesAutoresizingMaskIntoConstraints = false
        viewsDictionary[key] = view
    }

    addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: viewsDictionary))

}
}
Thomas22
  • 225
  • 1
  • 3
  • 11

2 Answers2

0

In general to make swift classes available in objective-c.
Also helpful for me was another question/answer on stack.

But give a thought to fundamental changes: it may be better, to create a new app in swift and connect to the old classes via the brindging header. The documentation is really helpful.

In future, it would be easier to add new elements in swift and use new functionallity.

f.overflow
  • 298
  • 1
  • 9
0

It's simply not safe to do this from within viewDidLoad in a view controller, and you shouldn't even attempt it.

That's because it is possible for viewDidLoad to be executed multiple times.

If that happens, it's going to replace your window and rootViewController with new instances in the middle of your app's execution (looking to the user like the app has reset itself).

You need to initialize the window and root controller from the app delegate, there's no choice.

However, you can still write most of the code in Swift.

First, create a Swift extension on UIWindow that includes a class function which initialises and configures your window, then returns it. Make sure that extension is accessible by Objective-C by adding @objc to the declaration:

@objc extension UIWindow {
    class func setRootHomeViewController() -> UIWindow {
        let window = UIWindow(frame: UIScreen.main.bounds)
        window.makeKeyAndVisible()
        let layout = UICollectionViewFlowLayout()
        window.rootViewController = UINavigationController(rootViewController: HomeController(collectionViewLayout: layout))
        return window;
    }
}

In your Objective-C app delegate, you then need to import your Swift header and call the new UIWindow class method that you've defined. Assign the returned window object to the app delegate's window property.

#import "YourProjectName-Swift.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    _window = [UIWindow setRootHomeViewController];

    return YES;
}

@end

It's only one line of Objective-C code, but it's necessary as the app delegate is the only safe place to do this.

Pete Morris
  • 1,472
  • 8
  • 13