I am learning auto layout in iOS8 by using SnapKit . I got lot of errors while applying constraints to cell subviews. below is the code used as subview to cell.contentview.
class FanFeedDynamicCellView: UIView{
var fanProfileImageView:UIImageView?
var fanNameLabel:UILabel?
var contentLabel:UILabel?
var thumbnailImageView:UIImageView?
var spacierView_FanProfile:UIView?
override init(frame: CGRect) {
super.init(frame : frame)
setupViewProperties()
}
convenience init () {
self.init(frame:CGRect.zero)
}
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
func setupViewProperties()
{
//1 add fanProfileImageView
fanProfileImageView = UIImageView()
fanProfileImageView!.image = UIImage(named: "avatar")
// setBorder(fanProfileImageView!)
self.addSubview(fanProfileImageView!)
//2 add Fan Name Label
fanNameLabel = UILabel()
fanNameLabel!.lineBreakMode = .ByTruncatingTail
fanNameLabel!.numberOfLines = 1
fanNameLabel!.textAlignment = .Left
fanNameLabel!.textColor = UIColor.blackColor()
fanNameLabel!.backgroundColor = UIColor(red: 0, green: 0, blue: 1, alpha: 0.1) // light blue
self.addSubview(fanNameLabel!)
//3 add ContentLabel
contentLabel = UILabel()
contentLabel!.lineBreakMode = .ByTruncatingTail
contentLabel!.numberOfLines = 0
contentLabel!.textAlignment = .Left
contentLabel!.textColor = UIColor.darkGrayColor()
contentLabel!.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.1) // light red
self.addSubview(contentLabel!)
//4 add Thumbnail View
thumbnailImageView = UIImageView()
// setBorder(thumbnailImageView!)
thumbnailImageView!.contentMode = .ScaleAspectFit
thumbnailImageView!.image = UIImage(named: "avatar")
self.addSubview(thumbnailImageView!)
updateFonts()
//Constraints for subviews
//setupConstraintsForProperties()
}
func updateFonts()
{
fanNameLabel!.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
contentLabel!.font = UIFont.preferredFontForTextStyle(UIFontTextStyleCaption2)
}
override func updateConstraints()
{
let padding:UIEdgeInsets = UIEdgeInsetsMake(10, 10, 10, 10)
fanProfileImageView!.snp_makeConstraints { (make) -> Void in
make.top.equalTo(self.snp_top).offset(padding.top)
make.left.equalTo(self.snp_left).offset(padding.left)
make.width.height.equalTo(60.0)
make.bottom.lessThanOrEqualTo(thumbnailImageView!.snp_top).offset(-padding.bottom)
}
fanNameLabel!.snp_makeConstraints { (make) -> Void in
make.top.equalTo(self.snp_top).offset(padding.top)
make.left.equalTo(fanProfileImageView!.snp_right).offset(padding.right)
make.right.equalTo(self.snp_right).offset(-padding.right)
// make.bottom.lessThanOrEqualTo(contentLabel!.snp_top).offset(-padding.top)
make.height.equalTo(20)
}
contentLabel!.snp_makeConstraints { (make) -> Void in
make.top.equalTo(fanNameLabel!.snp_bottom).offset(padding.top)
make.left.equalTo(fanProfileImageView!.snp_right).offset(padding.left)
make.right.equalTo(self.snp_right).offset(-padding.right)
// make.bottom.lessThanOrEqualTo(thumbnailImageView!.snp_top).offset(-padding.bottom)
// make.height.greaterThanOrEqualTo(20)
}
thumbnailImageView!.snp_makeConstraints { (make) -> Void in
make.top.greaterThanOrEqualTo(contentLabel!.snp_bottom).offset(padding.top)
// make.left.equalTo(padding.left)
make.bottom.lessThanOrEqualTo(-padding.bottom)
make.height.greaterThanOrEqualTo(20)
make.centerX.equalTo(self.snp_centerX) }
super.updateConstraints()
}
func setBorder(cView:UIView) -> UIView
{
let cLayer : CALayer = cView.layer
cLayer.borderColor = UIColor.redColor().CGColor
cLayer.borderWidth = 0.5
return cView
}
override func layoutSubviews() {
super.layoutSubviews()
fanNameLabel!.contentHuggingPriorityForAxis(.Vertical)
fanNameLabel!.contentCompressionResistancePriorityForAxis(.Vertical)
contentLabel!.contentHuggingPriorityForAxis(.Vertical)
contentLabel!.contentCompressionResistancePriorityForAxis(.Vertical)
}
The output would be the same as attached image . here we use on profile Image in LeftSide . User Name on top of label. Content label marked in light orange color would be the multiline. below this i attached the ImageView. when i scroll the tableview the height of the cell is unpredictable(layout changes automatically). would help me to correct the constraint to achieve the this output. For first launch the multiline cell would be in One line . once i goes to invisible the come again visible it adopt for full label content
You add more constraints than you had to. Also you don't really need to use contentHuggingPriority and contentCompressionResistance to achieve what you want.
This is how to make it work:
FanFeedDynamicCellView
class FanFeedDynamicCellView: UIView {
let fanProfileImageView = UIImageView()
let fanNameLabel = UILabel()
let contentLabel = UILabel()
let thumbnailImageView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
setupConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
fanProfileImageView.image = UIImage(named: "avatar")
addSubview(fanProfileImageView)
fanNameLabel.lineBreakMode = .ByTruncatingTail
fanNameLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
fanNameLabel.numberOfLines = 1
fanNameLabel.textAlignment = .Left
fanNameLabel.textColor = UIColor.blackColor()
fanNameLabel.backgroundColor = UIColor(red: 0, green: 0, blue: 1, alpha: 0.1) // light blue
addSubview(fanNameLabel)
contentLabel.lineBreakMode = .ByTruncatingTail
contentLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleCaption2)
contentLabel.numberOfLines = 0
contentLabel.textAlignment = .Left
contentLabel.textColor = UIColor.darkGrayColor()
contentLabel.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.1) // light red
addSubview(contentLabel)
thumbnailImageView.contentMode = .ScaleAspectFit
thumbnailImageView.image = UIImage(named: "thumbnail.jpg")
thumbnailImageView.backgroundColor = UIColor(red: 0, green: 1, blue: 0, alpha: 0.2) // light green
addSubview(thumbnailImageView)
}
func setupConstraints() {
let padding:UIEdgeInsets = UIEdgeInsetsMake(10, 10, 10, 10)
fanProfileImageView.snp_makeConstraints { (make) -> Void in
make.top.equalTo(padding.top)
make.left.equalTo(padding.left)
make.width.height.equalTo(60)
make.bottom.lessThanOrEqualTo(-padding.bottom)
}
fanNameLabel.snp_makeConstraints { (make) -> Void in
make.top.equalTo(fanProfileImageView)
make.left.equalTo(fanProfileImageView.snp_right).offset(padding.right)
make.right.equalTo(-padding.right)
}
contentLabel.snp_makeConstraints { (make) -> Void in
make.top.equalTo(fanNameLabel.snp_bottom).offset(padding.top)
make.left.right.equalTo(fanNameLabel)
}
thumbnailImageView.snp_makeConstraints { (make) -> Void in
make.top.greaterThanOrEqualTo(contentLabel.snp_bottom).offset(padding.top)
make.top.greaterThanOrEqualTo(fanProfileImageView.snp_bottom).offset(padding.top)
make.left.equalTo(fanProfileImageView)
make.right.equalTo(fanNameLabel)
make.bottom.equalTo(-padding.bottom)
}
}
}
I don't know why you created a UIView subclass instead of a UITableViewCell subclass for this, but I kept it that way and just added this view to a custom UITableViewCell subclass
FanFeedTableViewCell
This only adds the FanFeedDynamicCellView to its contentView:
class FanFeedTableViewCell: UITableViewCell {
let fanFeedView = FanFeedDynamicCellView()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .Subtitle, reuseIdentifier: reuseIdentifier)
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView() {
contentView.addSubview(fanFeedView)
fanFeedView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(contentView)
}
}
}
And that's it! Now just use this FanFeedTableViewCell in your UITableView and set the texts and images in cellForRowAtIndexPath.
Don't forget to do this with your table view to enable dynamic cell heights:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100 // or whatever your estimated row height is. This does not have to be precise
This is how it looks:
Related
I want to create a Bordered Rectangle with a Text/Label positioned in the top-left with AppKit that looks like:
The text view should have the same border and background color as the rectangle's border, and a padding around the actual text, not a fixed width/height constant.
I rewrote the above view in AppKit with NSTextView, but I can't see the textview, just the border:
class JumperView: NSView {
let label: String
private let borderWidth: CGFloat = 4
private let borderColor = NSColor(red: 250 / 255, green: 255 / 255, blue: 0 / 255, alpha: 1).cgColor
private let cornerRadius: CGFloat = 8
private var labelView: NSTextView!
required init(frame: NSRect, label: String) {
self.label = label
super.init(frame: frame)
wantsLayer = true
layer!.borderWidth = borderWidth
layer!.borderColor = borderColor
layer!.cornerRadius = cornerRadius
setupLabelView()
self.translatesAutoresizingMaskIntoConstraints = false
self.labelView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
self.labelView.topAnchor.constraint(equalTo: self.topAnchor),
self.labelView.leftAnchor.constraint(equalTo: self.leftAnchor),
])
}
private func setupLabelView() {
labelView = NSTextView()
labelView.string = label
labelView.font = .systemFont(ofSize: 24, weight: .bold)
// labelView.textContainerInset = .init(width: 8, height: 4)
labelView.wantsLayer = true
labelView.layer!.borderWidth = borderWidth
labelView.layer!.borderColor = borderColor
labelView.layer!.cornerRadius = cornerRadius
labelView.layer!.backgroundColor = borderColor
labelView.backgroundColor = NSColor(cgColor: borderColor)!
addSubview(labelView)
}
override init(frame frameRect: NSRect) {
fatalError()
}
required init?(coder: NSCoder) {
fatalError()
}
}
I'm not sure why the text view isn't showing up. Would appreciate the help!
I'm trying to create blur effect on a view and than add a shape which will show image on this blurred layer (custom video editing functionality)
Currently I'm able to do it only dragging mask view from the right edge:
but when I try to do it from the left edge, I get such a effect:
func configureBlurView() {
let viewHeight: CGFloat = 60
let padding: CGFloat = 10
blurView = UIView()
blurView.layer.cornerRadius = 10
blurView.clipsToBounds = true
blurView.isHidden = true
blurView.translatesAutoresizingMaskIntoConstraints = false
addSubview(blurView)
addConstraints([
blurView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: padding),
blurView.bottomAnchor.constraint(equalTo: stackView.topAnchor, constant: -padding),
blurView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -padding),
blurView.heightAnchor.constraint(equalToConstant: viewHeight)
])
addBlurEffect(for: blurView)
}
private func addBlurEffect(for view: UIView) {
let blurEffect = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
blurEffect.alpha = 0.5
blurEffect.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(blurEffect)
addConstraints([
blurEffect.topAnchor.constraint(equalTo: view.topAnchor),
blurEffect.leadingAnchor.constraint(equalTo: view.leadingAnchor),
blurEffect.bottomAnchor.constraint(equalTo: view.bottomAnchor),
blurEffect.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
private func makeClearHole(rect: CGRect) {
let maskLayer = CAShapeLayer()
maskLayer.fillColor = UIColor.black.cgColor
let pathToOverlay = CGMutablePath()
pathToOverlay.addRect(blurView.bounds)
pathToOverlay.addRect(rect)
maskLayer.path = pathToOverlay
maskLayer.fillRule = .evenOdd
maskLayer.cornerRadius = 10
blurView.layer.mask = maskLayer
}
I'm using touchesMoved method to change orange view dimensions:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard trimmerView.isHidden == false else { return }
if let touch = touches.first{
let currentTouchPoint = touch.location(in: self)
let previousTouchPoint = touch.previousLocation(in: self)
let deltaX = currentTouchPoint.x - previousTouchPoint.x
if trimmerView.bounds.width >= 70 {
if touchStartEdge.middle {
if trimmerViewLeadingConstraint.constant < 10 {
trimmerViewLeadingConstraint.constant = 10
} else if trimmerViewTrailingConstraint.constant > -10 {
trimmerViewTrailingConstraint.constant = -10
} else {
trimmerViewLeadingConstraint.constant += deltaX
trimmerViewTrailingConstraint.constant += deltaX
}
}
if touchStartEdge.leftEdge {
if trimmerViewLeadingConstraint.constant >= 10.0 {
trimmerViewLeadingConstraint.constant += deltaX
} else if trimmerViewLeadingConstraint.constant < 10.0 {
trimmerViewLeadingConstraint.constant = 10
}
}
if touchStartEdge.rightEdge {
if trimmerViewTrailingConstraint.constant <= -10 {
trimmerViewTrailingConstraint.constant += deltaX
} else if trimmerViewTrailingConstraint.constant > -10 {
trimmerViewTrailingConstraint.constant = -10.0
}
}
}
updateProgressBarConstraints()
makeClearHole(rect: CGRect(x: 0, y: 0, width: trimmerView.frame.width, height: trimmerView.frame.height))
UIView.animate(withDuration: 0.10, delay: 0, options: .curveEaseIn) { [weak self] in
self?.layoutIfNeeded()
}
}
}
What I'd like to achieve is to remove blur effect only in bounds of orange view.
Any ideas ?? :)
Thanks for help!!
Couple ways to do this - here's one...
Add a mask to the blur effect view. As the user drags the "trimmer" update the mask.
Here's a quick example...
We'll:
create a stack view with 10 images
overlay that with a masked blur effective view
add a "draggable trimmer view"
when we drag the trimmer, we update the mask
Example View Controller
class TrimmerVC: UIViewController {
var blurView: MaskedBlurView!
let trimmerView = DragView()
let stackView = UIStackView()
override func viewDidLoad() {
super.viewDidLoad()
// respect safe area when we setup constraints
let g = view.safeAreaLayoutGuide
stackView.distribution = .fillEqually
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
stackView.heightAnchor.constraint(equalToConstant: 80.0),
])
// let's add 10 imageviews to the stack view
for i in 1...10 {
if let img = UIImage(systemName: "\(i).circle.fill") {
let imgView = UIImageView(image: img)
imgView.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
stackView.addArrangedSubview(imgView)
}
}
let blurEffect = UIBlurEffect(style: .dark)
blurView = MaskedBlurView(effect: blurEffect)
blurView.alpha = 0.5
blurView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(blurView)
NSLayoutConstraint.activate([
blurView.topAnchor.constraint(equalTo: stackView.topAnchor, constant: 0.0),
blurView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 0.0),
blurView.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: 0.0),
blurView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 0.0),
])
trimmerView.backgroundColor = .systemOrange
view.addSubview(trimmerView)
trimmerView.didDrag = { [weak self] newX in
guard let self = self else { return }
self.blurView.clearX = newX - self.stackView.frame.origin.x
}
}
// we'll use this to update the framing when the stack view width changes
// such as on device rotation
var curStackW: CGFloat = -1
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if curStackW != stackView.frame.width {
curStackW = stackView.frame.width
var r = stackView.frame
r.origin.y += r.size.height + 20.0
r.size.width = 160
r.size.height = 40
trimmerView.frame = r
blurView.clearWidth = trimmerView.frame.width
blurView.clearX = 0
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// toggle the trimmer view between
// below the stack view and
// overlaid on the stack view
if trimmerView.frame.origin.y > stackView.frame.origin.y {
let r = stackView.frame
trimmerView.frame.origin.y = r.origin.y - 6.0
trimmerView.frame.size.height = r.height + 12.0
} else {
let r = stackView.frame
trimmerView.frame.origin.y = r.origin.y + r.height + 12.0
trimmerView.frame.size.height = 60.0
}
}
}
Example Draggable "Trimmer" View
class DragView: UIView {
var didDrag: ((CGFloat) -> ())?
let maskLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
maskLayer.fillColor = UIColor.red.cgColor
layer.mask = maskLayer
}
override func layoutSubviews() {
super.layoutSubviews()
let pathToOverlay = CGMutablePath()
pathToOverlay.addRect(bounds)
pathToOverlay.addRect(bounds.insetBy(dx: 20.0, dy: 8.0))
maskLayer.path = pathToOverlay
maskLayer.fillRule = .evenOdd
maskLayer.cornerRadius = 10
}
var touchStartX: CGFloat = 0
var frameStartX: CGFloat = 0
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
touchStartX = touch.location(in: self.superview!).x
frameStartX = self.frame.origin.x
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let loc = touch.location(in: self.superview!)
self.frame.origin.x = frameStartX + (loc.x - touchStartX)
didDrag?(self.frame.origin.x)
}
}
Example Masked Blur View
class MaskedBlurView: UIVisualEffectView {
public var clearWidth: CGFloat = 100 {
didSet { updateMask() }
}
public var clearX: CGFloat = 0 {
didSet { updateMask() }
}
private let maskLayer = CAShapeLayer()
override init(effect: UIVisualEffect?) {
super.init(effect: effect)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
maskLayer.fillColor = UIColor.red.cgColor
layer.mask = maskLayer
}
func updateMask() {
let leftR = CGRect(x: 0, y: 0, width: clearX, height: bounds.height)
let rightR = CGRect(x: clearX + clearWidth, y: 0, width: bounds.width, height: bounds.height)
let bez = UIBezierPath(rect: leftR)
bez.append(UIBezierPath(rect: rightR))
maskLayer.path = bez.cgPath
}
override func layoutSubviews() {
super.layoutSubviews()
maskLayer.frame = bounds
}
}
When running (in landscape orientation) it will start like this:
I placed the "trimmer" view below the stack view to make it a little more clear what's happening.
As we drag the trimmer view, the blur view's mask will be updated:
Tapping anywhere in an empty part of the screen will toggle the trimmer view between "under the stack view" and "overlaid on the stack view":
This was just put together quickly -- you should have no trouble restructuring the code to wrap everything into a single custom view (or however it would work best for your needs).
Hello I'm new into developing and right now I'm working on an AR application which works with image tracking and it should play different videos on different tracked images.
Right now the video stops, how can I loop the video?
Also if the image is not in the camera view, the video continues. Is there a pause function too?
I would be thankful for every hints.
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
#IBOutlet var sceneView: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let configuration = ARImageTrackingConfiguration()
if let trackedImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: Bundle.main) {
configuration.trackingImages = trackedImages
configuration.maximumNumberOfTrackedImages = 2
}
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
sceneView.session.pause()
}
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
let node = SCNNode()
if let imageAnchor = anchor as? ARImageAnchor {
let size = imageAnchor.referenceImage.physicalSize
var videoNode = SKVideoNode()
switch imageAnchor.name {
case "Image1":
videoNode = SKVideoNode(fileNamed: "Image1.mp4")
case "Image2":
videoNode = SKVideoNode(fileNamed: "Image2.mp4")
case "Image3":
videoNode = SKVideoNode(fileNamed: "Image3.mp4")
default:
break
}
videoNode.play()
let videoScene = SKScene(size: CGSize(width: 1280, height: 960))
videoScene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
videoScene.addChild(videoNode)
let plane = SCNPlane(width: size.width, height: size.height)
plane.firstMaterial?.diffuse.contents = videoScene
let planeNode = SCNNode(geometry: plane)
plane.firstMaterial?.isDoubleSided = true
planeNode.eulerAngles.x = .pi / 2
node.addChildNode(planeNode)
return node
}
return nil
}
}
I am developing an audio visualizer MacOS app, and I want to use Quartz/CoreGraphics to render the time-varying spectrum coordinated with the playing audio. My Renderer code is:
import Cocoa
class Renderer: NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
NSColor.white.setFill()
bounds.fill()
guard let context = NSGraphicsContext.current?.cgContext else {return}
var x : CGFloat = 0.0
var y : CGFloat = 0.0
context.beginPath()
context.move(to: CGPoint(x: x, y: y))
for bin in 0 ..< 300 {
x = CGFloat(bin)
y = CGFloat(Global.spectrum[bin])
context.addLine(to: CGPoint(x: x, y: y))
}
context.setStrokeColor(CGColor( red: 1, green: 0, blue: 0, alpha: 1))
context.setLineWidth(1.0)
context.strokePath()
self.setNeedsDisplay(dirtyRect)
}
}
This draws the path once - using the initial all-zeroes values of the spectrum[] array - and then continues to draw that same all-zeroes line indefinitely. It does not update using the new values in the spectrum[] array. I used a print() statement to verify that the values themselves are being updated, but the draw function does not redraw the path using the updated spectrum values. What am I doing wrong?
The following demo shows how to update an NSView with random numbers created by a timer in a separate class to hopefully mimic your project. It may be run in Xcode by setting up a Swift project for MacOS, copy/pasting the source code into a new file called 'main.swift', and deleting the AppDelegate supplied by Apple. A draw function similar to what you posted is used.
import Cocoa
var view : NSView!
var data = [Int]()
public extension Array where Element == Int {
static func generateRandom(size: Int) -> [Int] {
guard size > 0 else {
return [Int]()
}
return Array(0..<size).shuffled()
}
}
class DataManager: NSObject {
var timer:Timer!
#objc func fireTimer() {
data = Array.generateRandom(size:500)
view.needsDisplay = true
}
func startTimer(){
timer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(fireTimer), userInfo: nil, repeats: true)
}
func stopTimer() {
timer?.invalidate()
}
}
let dataMgr = DataManager()
class View: NSView {
override func draw(_ rect: NSRect) {
super.draw(rect)
NSColor.white.setFill()
bounds.fill()
guard let gc = NSGraphicsContext.current?.cgContext else {return}
var xOld : CGFloat = 0.0
var yOld : CGFloat = 0.0
var xNew : CGFloat = 0.0
var yNew : CGFloat = 0.0
var counter : Int = 0
gc.beginPath()
gc.move(to: CGPoint(x: xOld, y: yOld))
for i in 0 ..< data.count {
xNew = CGFloat(counter)
yNew = CGFloat(data[i])
gc.addLine(to: CGPoint(x: xNew, y: yNew))
xOld = xNew;
yOld = yNew;
counter = counter + 1
}
gc.setStrokeColor(CGColor( red: 1, green: 0, blue: 0, alpha: 1))
gc.setLineWidth(1.0)
gc.strokePath()
}
}
class ApplicationDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
#objc func myStartAction(_ sender:AnyObject ) {
dataMgr.startTimer()
}
#objc func myStopAction(_ sender:AnyObject ) {
dataMgr.stopTimer()
}
func buildMenu() {
let mainMenu = NSMenu()
NSApp.mainMenu = mainMenu
// **** App menu **** //
let appMenuItem = NSMenuItem()
mainMenu.addItem(appMenuItem)
let appMenu = NSMenu()
appMenuItem.submenu = appMenu
appMenu.addItem(withTitle: "Quit", action:#selector(NSApplication.terminate), keyEquivalent: "q")
}
func buildWnd() {
data = Array.generateRandom(size: 500)
let _wndW : CGFloat = 800
let _wndH : CGFloat = 600
window = NSWindow(contentRect: NSMakeRect( 0, 0, _wndW, _wndH ), styleMask:[.titled, .closable, .miniaturizable, .resizable], backing: .buffered, defer: false)
window.center()
window.title = "Swift Test Window"
window.makeKeyAndOrderFront(window)
// **** Start Button **** //
let startBtn = NSButton (frame:NSMakeRect( 30, 20, 95, 30 ))
startBtn.bezelStyle = .rounded
startBtn.title = "Start"
startBtn.action = #selector(self.myStartAction(_:))
window.contentView!.addSubview (startBtn)
// **** Stop Button **** //
let stopBtn = NSButton (frame:NSMakeRect( 230, 20, 95, 30 ))
stopBtn.bezelStyle = .rounded
stopBtn.title = "Stop"
stopBtn.action = #selector(self.myStopAction(_:))
window.contentView!.addSubview (stopBtn)
// **** Custom view **** //
view = View( frame:NSMakeRect(20, 60, _wndW - 40, _wndH - 80))
view.autoresizingMask = [.width, .height]
window.contentView!.addSubview (view)
// **** Quit btn **** //
let quitBtn = NSButton (frame:NSMakeRect( _wndW - 50, 10, 40, 40 ))
quitBtn.bezelStyle = .circular
quitBtn.autoresizingMask = [.minXMargin,.maxYMargin]
quitBtn.title = "Q"
quitBtn.action = #selector(NSApplication.terminate)
window.contentView!.addSubview(quitBtn)
}
func applicationDidFinishLaunching(_ notification: Notification) {
buildMenu()
buildWnd()
}
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
}
let applicationDelegate = ApplicationDelegate()
// **** main.swift **** //
let application = NSApplication.shared
application.setActivationPolicy(NSApplication.ActivationPolicy.regular)
application.delegate = applicationDelegate
application.activate(ignoringOtherApps:true)
application.run()
I have UIView subclass, for example, this:
#IBDesignable
class GradientView: UIView {
#IBInspectable var firstColor: UIColor = UIColor.red
#IBInspectable var secondColor: UIColor = UIColor.green
#IBInspectable var vertical: Bool = true
override func awakeFromNib() {
super.awakeFromNib()
applyGradient()
}
func applyGradient() {
let colors = [firstColor.cgColor, secondColor.cgColor]
let layer = CAGradientLayer()
layer.colors = colors
layer.frame = self.bounds
layer.startPoint = CGPoint(x: 0, y: 0)
layer.endPoint = vertical ? CGPoint(x: 0, y: 1) : CGPoint(x: 1, y: 0)
self.layer.addSublayer(layer)
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
applyGradient()
}
}
It successfully renders in Interface Builder for a new project, but it doesn't work for my "old" project.
Does anyone know why it happens?