IBDesignable View - need ViewController at design time - swift

I'm creating an IBDesignable component so that I can see the component render live in the Xcode storyboard. The component is simply a composition of other components. Unfortunately, one of those components requires a UIViewController to function properly. Yes, bad, but I have no control over it.
The component works fine running in the app, but does not render properly at design time because the UIViewController for that scene is not available. I have tried
marching up the responder chain looking for a UIViewController
creating an IBOutlet on the component and connecting it to the View
Controller in the storyboard
Neither of these has worked; the View Controller is always nil. Any suggestions?

A UIViewController can not be render in IBDesignable Mode.
You just can IBDesignable in a view staff.
sample:
import UIKit
#IBDesignable
class SMButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = 6
layer.borderColor = UIColor.baseInstagram.cgColor
layer.borderWidth = 0.66
titleLabel?.font = UIFont.systemFont(ofSize: 15)
titleLabel?.textColor = UIColor.baseInstagram
}
}
The sample is a round button with border.

Unfortunately none of the IBDesignable component's surrounding environment can influence the component itself.

Related

UINavigationBar bleeding its background when PageSheet is animating

My application has a UIViewController where uses the new .pageSheet modal presentation style introduced on iOS 13.
This UIViewController has a UINavigationBar on the top and it's pinned, by constraints, at the top, leading and trailing.
I noticed that the background from this view bleeds in white while the UIViewController is animating. Take a look on this recorded GIF from a real device:
Is there anything I can do to solve this? The UIViewController and UINavigationBar were created programatically.
Maybe doing this can solve it?
override func layoutSubviews() {
super.layoutSubviews()
var originalFrame = frame
frame = originalFrame
}
I am using Swift 5.1 and Xcode 11.3. The iPhone is running iOS 13.1.3.
Probably you set the view's layer to rasterize, this is making with the .pageSheet animation not draw it correctly.
The solution, remove the code below:
navigationBar.layer.shouldRasterize = true
navigationBar.layer.rasterizationScale = UIScreen.main.scale

Is IBDesignable broken for AppKit in Xcode 9.2 and Swift 4?

Most questions, and answers related to this, are based on older versions of both Xcode and Swift. Additionally, 90 percent of the questions relate to UIKit and drawing custom controls.
I am adding a standard button, that is centered inside a custom control, decorated with IBDesignable.
import Cocoa
#IBDesignable public class ButtonPresetView: NSView {
public override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
initialControlSetup()
}
public required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
initialControlSetup()
}
private func initialControlSetup() {
let button = NSButton(title: "Hello", target: nil, action: nil)
button.translatesAutoresizingMaskIntoConstraints = false
addSubview(button)
// Configure button
centerXAnchor.constraint(equalTo: button.centerXAnchor).isActive = true
centerYAnchor.constraint(equalTo: button.centerYAnchor).isActive = true
}
}
I add a custom view to the application and set the class property in the Identity Inspector to my custom class (ButtonPresetView).
It should show the button centered on the canvas, but the canvas is blank.
Not sure many people use it this way, but it worked gloriously with Swift 3 in Xcode 8.3.
Does anyone else have this problem?
I was able to get this to work in the latest Xcode by adding the following two lines to the top of the initialControlSetup function:
wantsLayer = true
canDrawSubviewsIntoLayer = true
I think this basically tells the NSView to render in a way that is more similar to how iOS works. If this worked in Xcode 8.3 as you say, it's possible that Apple introduced this regression in Xcode 9 without realizing it.
Dave's answer is correct, I just want to make a note on the consequences of this solution.
When canDrawSubviewsIntoLayer is set to true, all its sub views, that did not enable wantsLayer specifically, will render its contents using the layer of the parent view with canDrawSubviewsIntoLayer set to true.
This means sub view animations is disabled, since they lack a backing layer of their own. To prevent this from happening during runtime, you can put canDrawSubviewsIntoLayer = true into the prepareForInterfaceBuilder() function.
On a curious note, Interface Builder does not render the control, if you explicitly set button.wantsLayer = true, which according to the "canDrawSubviewsIntoLayer" documentation, should give the control its own backing layer and not render itself into the parent layer.
This is purely speculation, but I'm guessing as an optimisation, Interface Builder only renders the top layers/controls of the content view.

swift - frame issue in UINavigationController class when rotating

I've used a custom UINavigationController class to put a colour gradient across the navbar. The custom navclass determines the frame size of the navbar, then puts the gradient inside. Problem is, when I rotate from portrait to landscape, the gradient only fills the portrait portion of the landscape bar. I've only assigned the custom UINavigationController class to the navigation view in the storyboard.
So I'm guessing I somehow need to call a refresh for the frame size in the custom navclass when a rotation is done, but I'm not sure how or where?
Here's the relevant code snippet, from the override func viewDidLoad() of the custom navclass.
let gradientlayer = CAGradientLayer()
gradientLayer.frame = self.navigationBar.bounds
self.navigationBar.layer.inserSublayer(gradientLayer, atIndex: 1)
I tried putting it inside viewWillAppear(), but that didn't work. Any help?
viewDidLoad() and viewWillAppear() don't get called on rotation, so your code won't update the frame there.
You should add gradientLayer.frame = self.navigationBar.bounds to viewWillLayoutSubviews() but leave the other code in viewDidLoad() so you don't wind up with multiple gradient layers.

How can I call this function?

I am so stuck right now, I have tried everything which seams logical to me to make this work but having no luck...
I got this in a separate swift file:
import Foundation
import UIKit
class MyViewController: UIViewController {
func background() {
var imageView : UIImageView
imageView = UIImageView(frame:CGRectMake(0, 0, 100, 300));
imageView.image = UIImage(named:"bg.jpg")
self.view.addSubview(imageView)
}
}
I want to call it into a separate view controller file. The reason I am doing it like this is because then I can just call the background class in all my view controllers a and I don't have to do the same code in each one.
The ways I have tried calling it are:
MyViewController.background() - I get error Missing parameter for #1 in call
background() - I get error Use of unresolved identifier 'background'
MyViewController() - I don't get error but nothing happens.
I would really appreciate it if someone could tell me how I can call this function into my 'ViewDidLoad' part in the view controller.
Thank you.
You're barking up the wrong tree. Even if you could call the background method in MyViewController, this would still not accomplish what you are trying to accomplish, because when you call background in the "separate view controller file", the self in the background method would be the other view controller - and so you would be putting the UIImageView into the other view controller's view, not this view controller's view. If you want to be able to do the same thing separately in each view controller, you need to make the background method available internally in each of them.
The way to do that is to inject background through an extension into UIViewController itself, from which all your view controllers inherit:
extension UIViewController {
func background() {
var imageView = UIImageView(frame:CGRectMake(0, 0, 100, 300))
imageView.image = UIImage(named:"bg.jpg")
self.view.addSubview(imageView)
}
}
Now any view controller in your app can just say self.background() (or simply background()) to call this method.

swift iMessage style keyboard input

i am trying to implement an iMessage style keyboard input with a textview that sits at the bottom of the screen then slides up with the keyboard when you touch the textView, then is docked to the top of the keyboard.
I found MessageComposerView which is exactly what I want. Unfortunately I cannot get it working ( I am using swift).
below is my code:
import UIKit
class CommentsViewController: UIViewController, MessageComposerViewDelegate {
var messageComposerView: MessageComposerView!
override func viewDidLoad() {
super.viewDidLoad()
let defaultWidth = view.frame.size.width
let defaultHeight = CGFloat(54.0)
let subviewFrame = CGRect(x: 0, y: view.frame.height - defaultHeight, width: defaultWidth, height: defaultHeight)
messageComposerView = MessageComposerView(frame: subviewFrame) as MessageComposerView
view.addSubview(messageComposerView)
}
func messageComposerSendMessageClickedWithMessage(message: String!) {
}
}
however it does not show up. Ive printed the view and its frame is correct, its just that there is nothing there for some reason. Everything looks like it should be working. Does anyone see anything wrong with my current implementation?
Try PHFComposeBarView Library (https://github.com/fphilipe/PHFComposeBarView), it's a exact copy of the iMessage composer bar that can be used in C & Swift from storyboard or code
Code Example : https://github.com/liveminds/SwiftPHFComposeBarTest
Storyboard Example : https://github.com/liveminds/SwiftPHFComposeBarTest/tree/storyboard-managed
To add the bar to your view:
drag a new UIView on your UIViewcontroller, assign "PHFComposeBarView" class to UIView
Add an outlet of the UIView to your Viewcontroller's class
add "PHFComposeBarViewDelegate" to your Viewcontroller's class
assign UIView Delegate in viewdidload() : self.composerBarOutlet.delegate = self
Set the composer bar to appear as inputAccessoryView above the keyboard:
override var inputAccessoryView: UIView {
composerBar.removeFromSuperview()
return composerBar
}
Style your Bar(example):
composerBar.utilityButtonImage = UIImage(named: "fullStar")!
composerBar.buttonTitle = "Submit"
composerBar.maxCharCount = 200
composerBar.maxLinesCount = 5
composerBar.alpha = 1.0
composerBar.buttonTintColor = AppConfig.BLUECOLOR
composerBar.placeholder = "What do you think about this product?"
Try following this example by Andrew Bancroft Send Text Message In-App – Using MFMessageComposeViewController with Swift. His example provides a nice walkthrough using Swift and includes sample code on GitHub.
Just going from the code snippet included in your post, it looks like you need to import Foundation, import MessageUI, conform to the MFMessageComposeViewControllerDelegate protocol, and implement the messageComposeViewController protocol method. All of this is covered in Andrew's blog post. This should give you what you're looking for.
Have you find the answer yet?
I use the same Framework however I believe you need to set up the delegate to self. IN my case it fails though I dont know why but it is written down in the readme github.