I am using Xcode 14.2 with both a SwiftUI file as well as an Objective-C file. It uses NotificationCenter to change my text and labels that I have within my SwiftUI. Part of my text (fourLinesText) is being cut off when it runs on an iPhone 8 but am unable to see it because the preview will not display anything and gives an error stating:
‘@objc’ can only be applied to an extension of a class
I have tried changing the class to @Objc extension class but am given the error: Expected type name in extension declaration
How can I restore the preview so I can see my app run in it?
Swift code:
import Foundation
import SwiftUI
@objc public protocol CMWUploadViewV2Delegate: NSObjectProtocol {
@objc func uploadOKButtonPushed()
@objc func uploadAbortButtonPushed()
@objc func uploadErrorLogButtonPushed()
@objc func uploadRetryButtonPushed()
}
@objc class CMWSTUploadViewV2Controller: UIViewController {
@IBOutlet var ProgressBarView: UIView!
// embed SwiftUI into the UIKit storyboard view controller
override func viewDidLoad() {
super.viewDidLoad()
let childView = UIHostingController(rootView: ContentView())
addChild(childView)
childView.view.frame = ProgressBarView.bounds
ProgressBarView.addSubview(childView.view)
childView.didMove(toParent: self)
}
@objc weak var delegate: CMWUploadViewV2Delegate?
//creating function for dictionary to store changes and create notification
//This takes a value between 0 and 1
@objc func UpdateUploadProgress(progressValue: Float)
{
//First, we need to normalize the value which is between 0 and 0.99 to 0 and 0.8999996
let normalizedValue = progressValue * 0.8999996
let valueUpdate:[String: Float] = ["ProgressValue": normalizedValue]
NotificationCenter.default.post(name:NSNotification.Name("COM.CMW.UpdateUploadProgress"), object: nil, userInfo:valueUpdate)
}
@objc func UpdateInitialText(InitialTextParameter: String)
{
let textUpdate:[String: String] = ["InitialTextParameter": InitialTextParameter]
NotificationCenter.default.post(name:NSNotification.Name("COM.CMW.UpdateInitialText"), object: nil, userInfo:textUpdate)
}
@objc func UpdateAddressText(AddressTextParameter: String)
{
let textUpdate:[String: String] = ["AddressTextParameter": AddressTextParameter]
NotificationCenter.default.post(name:NSNotification.Name("COM.CMW.UpdateAddressText"), object: nil, userInfo:textUpdate)
}
@objc func UpdateOrangeText(OrangeTextParameter: String)
{
let textUpdate:[String: String] = ["OrangeTextParameter": OrangeTextParameter]
NotificationCenter.default.post(name:NSNotification.Name("COM.CMW.UpdateOrangeText"), object: nil, userInfo:textUpdate)
}
@objc func UpdateGreySubText(GreySubTextParameter: String)
{
let textUpdate:[String: String] = ["GreySubTextParameter": GreySubTextParameter]
NotificationCenter.default.post(name:NSNotification.Name("COM.CMW.UpdateGreySubText"), object: nil, userInfo:textUpdate)
}
@objc func UpdateControllerTypeText(ControllerTypeTextParameter: String)
{
let textUpdate:[String: String] = ["ControllerTypeTextParameter": ControllerTypeTextParameter]
NotificationCenter.default.post(name:NSNotification.Name("COM.CMW.UpdateControllerTypeText"), object: nil, userInfo:textUpdate)
}
@objc func UpdateConnectionTypeText(ConnectionTypeTextParameter: String)
{
let textUpdate:[String: String] = ["ConnectionTypeTextParameter": ConnectionTypeTextParameter]
NotificationCenter.default.post(name:NSNotification.Name("COM.CMW.UpdateConnectionTypeText"), object: nil, userInfo:textUpdate)
}
@objc func UpdateStatusText(StatusTextParameter: String)
{
let textUpdate:[String: String] = ["StatusTextParameter": StatusTextParameter]
NotificationCenter.default.post(name:NSNotification.Name("COM.CMW.UpdateStatusText"), object: nil, userInfo:textUpdate)
}
struct ContentView: View {
//creating vars
@State var progressValue: Float = 0.3
//let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
@State private var degress: Double = -110
//@State var GreySubText = ""
let label = UILabel()
@State var buttonText1 = ""
@State var buttonText2 = ""
@State var controllerTypeText = ""
@State var connectionTypeText = "CAN"
@State var statusText = "Status text here"
@State var fourLinesText = "More status text here. Controller responding. This text will be 4 lines in length. 1234567891011121314151617181920. Controller responding. This text will be 4 lines in length. 1234567891011121314151617181920. Controller responding. This text will be 4 lines in length. 1234567891011121314151617181920"
@State var initialText = "Resetting to upload mode."
//setting up notification center to listen to addresses
let publisher = NotificationCenter.default.publisher(for: NSNotification.Name("COM.CMW.UpdateUploadProgress"))
let publisher8 = NotificationCenter.default.publisher(for: NSNotification.Name("COM.CMW.UpdateInitialText"))
//let publisher6 = NotificationCenter.default.publisher(for: NSNotification.Name("COM.CMW.UpdateAddressText"))
//let publisher7 = NotificationCenter.default.publisher(for: NSNotification.Name("COM.CMW.UpdateOrangeText"))
//let publisher5 = NotificationCenter.default.publisher(for: NSNotification.Name("COM.CMW.UpdateGreySubText"))
let publisher2 = NotificationCenter.default.publisher(for: NSNotification.Name("COM.CMW.UpdateControllerTypeText"))
let publisher3 = NotificationCenter.default.publisher(for: NSNotification.Name("COM.CMW.UpdateConnectionTypeText"))
let publisher4 = NotificationCenter.default.publisher(for: NSNotification.Name("COM.CMW.UpdateStatusText"))
//display
var body: some View {
VStack {
Spacer()
.frame(height: 70)
if #available(iOS 16.0, *) {
Label(initialText, systemImage: "clock")
.font(.title)
.foregroundColor(Color(hex: "000"))
.fontWeight(.bold)
.onReceive(publisher8) { obj in
let initialTextVal = obj.userInfo?["InitialTextParameter"] as? String
initialText = initialTextVal!
}
} else {
// Fallback on earlier versions
Label(initialText, systemImage: "clock")
.font(.title)
.foregroundColor(Color(hex: "000"))
.onReceive(publisher8) { obj in
let initialTextVal = obj.userInfo?["InitialTextParameter"] as? String
initialText = initialTextVal!
}
}
ZStack{
//background rectangle
RoundedRectangle(cornerRadius: 25)
.fill(Color(hex: "494949"))
.frame(width: 300, height: 300)
ProgressBar(progress: self.$progressValue)
.frame(width: 250.0, height: 250.0)
.padding(40.0)
.onReceive(publisher) { obj in
let progVal = obj.userInfo?["ProgressValue"] as? Float
progressValue = progVal!
}
ProgressBarTriangle(progress: self.$progressValue).frame(width: 280.0, height: 290.0).rotationEffect(.degrees(degress), anchor: .bottom)
.offset(x: 0, y: -150)
}
//***Controller, Connection, and Status labels/text
Spacer()
.frame(height: 20)
VStack(alignment :.leading, spacing: 30) {
HStack {
if #available(iOS 16.0, *) {
Label("Controller type:", image: "ControllerIcon")
.foregroundColor(Color(hex: "000"))
.fontWeight(.bold)
} else {
// Fallback on earlier versions
Label("Controller type:", image: "ControllerIcon")
.foregroundColor(Color(hex: "000"))
}
Text(controllerTypeText)
//app crashes when following code active:
//onReceive(publisher2) { obj in
//let controllerTextVal = //obj.userInfo?["ControllerTypeTextParameter"] as? String
//controllerTypeText = controllerTextVal!
//}
}
HStack {
if #available(iOS 16.0, *) {
Label("Connection type:", image: "CANIconSmall")
.foregroundColor(Color(hex: "000"))
.fontWeight(.bold)
} else {
// Fallback on earlier versions
Label("Connection type:", image: "CANIconSmall")
.foregroundColor(Color(hex: "000"))
}
Text(connectionTypeText)
.onReceive(publisher3) { obj in
let connectionTextVal = obj.userInfo?["ConnectionTypeTextParameter"] as? String
connectionTypeText = connectionTextVal!
}
}
HStack {
if #available(iOS 16.0, *) {
Label("Status:", image: "StatusIcon")
.foregroundColor(Color(hex: "000"))
.fontWeight(.bold)
} else {
// Fallback on earlier versions
Label("Status:", image: "StatusIcon")
.foregroundColor(Color(hex: "000"))
}
}
//four lines of status text
Text(fourLinesText)
.foregroundColor(Color(hex: "000"))
.lineLimit(4)
.multilineTextAlignment(.leading)
.onReceive(publisher4) { obj in
let statusTextVal = obj.userInfo?["StatusTextParameter"] as? String
fourLinesText = statusTextVal!
}
Spacer()
}
.padding(.leading, 20)
.padding(.trailing, 20)
}
}
public struct ProgressBar: View {
//creating vars
@Binding var progress: Float
@State var addressText = "0x1c000"
@State var orangeText = "Orange Text"
@State var GreySubText = ""
let publisher7 = NotificationCenter.default.publisher(for:
NSNotification.Name("COM.CMW.UpdateOrangeText"))
let publisher6 = NotificationCenter.default.publisher(for: NSNotification.Name("COM.CMW.UpdateAddressText"))
let publisher5 = NotificationCenter.default.publisher(for: NSNotification.Name("COM.CMW.UpdateGreySubText"))
public func UpdateProgressValue(progressValue: Float)
{
progress = progressValue
}
var body: some View {
VStack (alignment :.center, spacing: 30){
HStack {
Label("Address:", systemImage: "target")
.foregroundColor(Color(hex: "8f8f8f"))
.padding(.top, 15)
Text(addressText)
.foregroundColor(Color(hex: "8f8f8f"))
.padding(.top, 15)
.onReceive(publisher6) { obj in
let addressTextVal = obj.userInfo?["AddressTextParameter"] as? String
addressText = addressTextVal!
}
}
ZStack {
Circle()
.trim(from: 0.35, to: 0.85)
.stroke(style: StrokeStyle(lineWidth: 12.0, lineCap: .round, lineJoin: .round))
.opacity(0.3)
.foregroundColor(Color.gray)
.rotationEffect(.degrees(54.5))
Circle()
.trim(from: 0.35, to: CGFloat(self.progress))
.stroke(style: StrokeStyle(lineWidth: 12.0, lineCap: .round, lineJoin: .round))
.fill(AngularGradient(gradient: Gradient(stops: [
.init(color: Color.init(hex: "daff00"), location: 0.39000002),
.init(color: Color.init(hex: "00ff04"), location: 0.5999999),
.init(color: Color.init(hex: "32E1A0"), location: 0.8099997)]), center: .center))
.rotationEffect(.degrees(54.5))
VStack(alignment :.center, spacing: 10) {
Text("824").font(Font.system(size: 44)).bold().foregroundColor(Color.init(hex: "ffffff"))
if #available(iOS 16.0, *) {
Label(orangeText, systemImage: "clock")
.foregroundColor(Color(hex: "FF6600"))
.fontWeight(.bold)
.onReceive(publisher7) { obj in
let statusTextVal = obj.userInfo?["OrangeTextParameter"] as? String
orangeText = statusTextVal!
}
} else {
// Fallback on earlier versions
Label(orangeText, systemImage: "clock")
.foregroundColor(Color(hex: "FF6600"))
.onReceive(publisher7) { obj in
let statusTextVal = obj.userInfo?["OrangeTextParameter"] as? String
orangeText = statusTextVal!
}
}
Text(GreySubText)
.foregroundColor(Color(hex: "8f8f8f"))
.multilineTextAlignment(.center)
.onReceive(publisher5) { obj in
let GreySubTextVal = obj.userInfo?["GreySubTextParameter"] as? String
GreySubText = GreySubTextVal!
}
}
}
}
}
}
struct ProgressBarTriangle: View {
@Binding var progress: Float
var body: some View {
ZStack {
Image("triangle").resizable().frame(width: 10, height: 10, alignment: .center)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
}
extension Color {
init(hex: String) {
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int: UInt64 = 0
Scanner(string: hex).scanHexInt64(&int)
let a, r, g, b: UInt64
switch hex.count {
case 3: // RGB (12-bit)
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
case 6: // RGB (24-bit)
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
case 8: // ARGB (32-bit)
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
(a, r, g, b) = (1, 1, 1, 0)
}
self.init(
.sRGB,
red: Double(r) / 255,
green: Double(g) / 255,
blue: Double(b) / 255,
opacity: Double(a) / 255
)
}
}
Objective C code:
-(void)summerTestUploadViewController
{
CMWSTUploadViewV2Controller* myView = [[UIStoryboard storyboardWithName:@"MainStoryboard_iPhone" bundle:nil] instantiateViewControllerWithIdentifier:@"ID_CMW_UPLOADVIEWV2_VIEW"];
// myView.delegate = self;
[self presentViewController:myView animated:YES completion:^{
[myView UpdateUploadProgressWithProgressValue:0.7];
[myView UpdateInitialTextWithInitialTextParameter:@"Initial text here"];
[myView UpdateAddressTextWithAddressTextParameter:@"Address text here"];
[myView UpdateOrangeTextWithOrangeTextParameter:@"Orange text here"];
[myView UpdateGreySubTextWithGreySubTextParameter:@"Grey sub text here"];
[myView UpdateControllerTypeTextWithControllerTypeTextParameter:@"Controller text here"];
[myView UpdateConnectionTypeTextWithConnectionTypeTextParameter:@"Connection text here"];
[myView UpdateStatusTextWithStatusTextParameter:@"Status text here. 4 lines of text will be allowed in this area. 4 lines of text will be allowed in this area. 4 lines of text will be allowed in this area. 4 lines of text will be allowed in this area. 4 lines of text will be allowed in this area."];
}];
}
2
Answers
When I remove the attribute @objc for the class CMWSTUploadViewV2Controller it allows me to view it in the preview pane to see how my constraints behave.
To address your issue with the SwiftUI preview not displaying and the error message about ‘@objc’ in Xcode 14.2, and to view your layout on an iPhone 8 for adjustments, here’s a summary of what you can do:
Fixing the ‘@objc’ Error: The error message "’@objc’ can only be applied to an extension of a class" suggests there’s a mismatch in how ‘@objc’ attributes are used in your code. Make sure they are only applied to members of an Objective-C compatible class or an extension of such a class.
Previewing on an iPhone 8:
GeometryReader
,flexible frames
, orScrollView
, to make your UI elements adjust dynamically to different screen sizes.General Tips:
By fixing the ‘@objc’ usage in your code and using the iPhone 8 simulator to test and adjust your layout, you should be able to address the text cutoff issue effectively. Remember to regularly test on different devices to ensure compatibility and a good user experience.
Let me know if it worked for you. 🙂